diff --git a/evm/src/cpu/kernel/asm/basic_macros.asm b/evm/src/cpu/kernel/asm/basic_macros.asm index 200aeea0..9e884fea 100644 --- a/evm/src/cpu/kernel/asm/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/basic_macros.asm @@ -9,18 +9,21 @@ %endmacro %macro pop2 - pop - pop + %rep 2 + pop + %endrep %endmacro %macro pop3 - pop - %pop2 + %rep 3 + pop + %endrep %endmacro %macro pop4 - %pop2 - %pop2 + %rep 4 + pop + %endrep %endmacro // If pred is zero, yields z; otherwise, yields nz diff --git a/evm/src/cpu/kernel/assembler.rs b/evm/src/cpu/kernel/assembler.rs index bdc8ded4..d1eaf752 100644 --- a/evm/src/cpu/kernel/assembler.rs +++ b/evm/src/cpu/kernel/assembler.rs @@ -44,6 +44,7 @@ pub(crate) fn assemble(files: Vec, constants: HashMap) -> Ke let mut local_labels = Vec::with_capacity(files.len()); for file in files { let expanded_file = expand_macros(file.body, ¯os); + let expanded_file = expand_repeats(expanded_file); let expanded_file = inline_constants(expanded_file, &constants); local_labels.push(find_labels(&expanded_file, &mut offset, &mut global_labels)); expanded_files.push(expanded_file); @@ -132,6 +133,21 @@ fn expand_macro_call( expand_macros(expanded_item, macros) } +fn expand_repeats(body: Vec) -> Vec { + let mut expanded = vec![]; + for item in body { + if let Item::Repeat(count, block) = item { + let reps = count.to_u256().as_usize(); + for _ in 0..reps { + expanded.extend(block.clone()); + } + } else { + expanded.push(item); + } + } + expanded +} + fn inline_constants(body: Vec, constants: &HashMap) -> Vec { body.into_iter() .map(|item| { @@ -157,8 +173,8 @@ fn find_labels( let mut local_labels = HashMap::::new(); for item in body { match item { - Item::MacroDef(_, _, _) | Item::MacroCall(_, _) => { - panic!("Macros should have been expanded already") + Item::MacroDef(_, _, _) | Item::MacroCall(_, _) | Item::Repeat(_, _) => { + panic!("Macros and repeats should have been expanded already") } Item::GlobalLabelDeclaration(label) => { let old = global_labels.insert(label.clone(), *offset); @@ -185,8 +201,8 @@ fn assemble_file( // Assemble the file. for item in body { match item { - Item::MacroDef(_, _, _) | Item::MacroCall(_, _) => { - panic!("Macros should have been expanded already") + Item::MacroDef(_, _, _) | Item::MacroCall(_, _) | Item::Repeat(_, _) => { + panic!("Macros and repeats should have been expanded already") } Item::GlobalLabelDeclaration(_) | Item::LocalLabelDeclaration(_) => { // Nothing to do; we processed labels in the prior phase. @@ -393,6 +409,13 @@ mod tests { assert_eq!(kernel.code, vec![push4, 0xDE, 0xAD, 0xBE, 0xEF]); } + #[test] + fn repeat() { + let kernel = parse_and_assemble(&["%rep 3 ADD %endrep"]); + let add = get_opcode("ADD"); + assert_eq!(kernel.code, vec![add, add, add]); + } + fn parse_and_assemble(files: &[&str]) -> Kernel { parse_and_assemble_with_constants(files, HashMap::new()) } diff --git a/evm/src/cpu/kernel/ast.rs b/evm/src/cpu/kernel/ast.rs index 5025cd99..9bb315ff 100644 --- a/evm/src/cpu/kernel/ast.rs +++ b/evm/src/cpu/kernel/ast.rs @@ -12,6 +12,8 @@ pub(crate) enum Item { MacroDef(String, Vec, Vec), /// Calls a macro: name, args. MacroCall(String, Vec), + /// Repetition, like `%rep` in NASM. + Repeat(Literal, Vec), /// Declares a global label. GlobalLabelDeclaration(String), /// Declares a label that is local to the current file. diff --git a/evm/src/cpu/kernel/evm_asm.pest b/evm/src/cpu/kernel/evm_asm.pest index d7f4629a..d5a89d99 100644 --- a/evm/src/cpu/kernel/evm_asm.pest +++ b/evm/src/cpu/kernel/evm_asm.pest @@ -15,9 +15,10 @@ literal = { literal_hex | literal_decimal } variable = ${ "$" ~ identifier } constant = ${ "@" ~ identifier } -item = { macro_def | macro_call | global_label | local_label | bytes_item | push_instruction | nullary_instruction } +item = { macro_def | macro_call | repeat | global_label | local_label | bytes_item | push_instruction | nullary_instruction } macro_def = { ^"%macro" ~ identifier ~ macro_paramlist? ~ item* ~ ^"%endmacro" } -macro_call = ${ "%" ~ !(^"macro" | ^"endmacro") ~ identifier ~ macro_arglist? } +macro_call = ${ "%" ~ !(^"macro" | ^"endmacro" | ^"rep" | ^"endrep") ~ identifier ~ macro_arglist? } +repeat = { ^"%rep" ~ literal ~ item* ~ ^"%endrep" } macro_paramlist = { "(" ~ identifier ~ ("," ~ identifier)* ~ ")" } macro_arglist = !{ "(" ~ push_target ~ ("," ~ push_target)* ~ ")" } global_label = { ^"GLOBAL " ~ identifier ~ ":" } diff --git a/evm/src/cpu/kernel/parser.rs b/evm/src/cpu/kernel/parser.rs index c6dd8392..b8ac3f40 100644 --- a/evm/src/cpu/kernel/parser.rs +++ b/evm/src/cpu/kernel/parser.rs @@ -23,6 +23,7 @@ fn parse_item(item: Pair) -> Item { match item.as_rule() { Rule::macro_def => parse_macro_def(item), Rule::macro_call => parse_macro_call(item), + Rule::repeat => parse_repeat(item), Rule::global_label => { Item::GlobalLabelDeclaration(item.into_inner().next().unwrap().as_str().into()) } @@ -70,6 +71,13 @@ fn parse_macro_call(item: Pair) -> Item { Item::MacroCall(name, args) } +fn parse_repeat(item: Pair) -> Item { + assert_eq!(item.as_rule(), Rule::repeat); + let mut inner = item.into_inner().peekable(); + let count = parse_literal(inner.next().unwrap()); + Item::Repeat(count, inner.map(parse_item).collect()) +} + fn parse_push_target(target: Pair) -> PushTarget { assert_eq!(target.as_rule(), Rule::push_target); let inner = target.into_inner().next().unwrap();