From 891373131bbae51bde155c1783b3ae1c6cfb06f3 Mon Sep 17 00:00:00 2001 From: d1ll0n Date: Thu, 30 Mar 2023 04:12:26 -0500 Subject: [PATCH 1/6] add test for basic macro macro arg --- huff_core/tests/macro_invoc_args.rs | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/huff_core/tests/macro_invoc_args.rs b/huff_core/tests/macro_invoc_args.rs index 1ddc850f..aff0d65e 100644 --- a/huff_core/tests/macro_invoc_args.rs +++ b/huff_core/tests/macro_invoc_args.rs @@ -308,3 +308,41 @@ fn test_bubbled_arg_with_different_name() { // Check the bytecode assert_eq!(main_bytecode, expected_bytecode); } + +#[test] +fn test_macro_macro_arg() { + let source = r#" + #define constant TWO = 0x02 + + #define macro MUL_BY_10() = takes(1) returns (1) { + 0x0a mul + } + + #define macro EXEC_WITH_VALUE(value, macro) = takes(0) returns(1) { + + } + + #define macro MAIN() = takes(0) returns(0) { + EXEC_WITH_VALUE(TWO, MUL_BY_10) + } + "#; + + // Lex + Parse + let flattened_source = FullFileSource { source, file: None, spans: vec![] }; + let lexer = Lexer::new(flattened_source.source); + let tokens = lexer.into_iter().map(|x| x.unwrap()).collect::>(); + let mut parser = Parser::new(tokens, None); + let mut contract = parser.parse().unwrap(); + contract.derive_storage_pointers(); + + let evm_version = EVMVersion::default(); + + // Create main and constructor bytecode + let main_bytecode = Codegen::generate_main_bytecode(&evm_version, &contract, None).unwrap(); + + // Full expected bytecode output (generated from huffc) (placed here as a reference) + let expected_bytecode = "6002600a02"; + + // Check the bytecode + assert_eq!(main_bytecode.to_lowercase(), expected_bytecode.to_lowercase()); +} From e11b31ce289c393471fd7a0dba7a06c07741804c Mon Sep 17 00:00:00 2001 From: d1ll0n Date: Thu, 30 Mar 2023 04:14:21 -0500 Subject: [PATCH 2/6] support basic macro macro args --- huff_codegen/src/irgen/arg_calls.rs | 67 ++++++++++++++++++++++++++++- huff_codegen/src/lib.rs | 3 ++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/huff_codegen/src/irgen/arg_calls.rs b/huff_codegen/src/irgen/arg_calls.rs index 09e81621..88e928e3 100644 --- a/huff_codegen/src/irgen/arg_calls.rs +++ b/huff_codegen/src/irgen/arg_calls.rs @@ -1,6 +1,8 @@ use huff_utils::prelude::*; use std::str::FromStr; +use crate::Codegen; + // Arguments can be literals, labels, opcodes, or constants // !! IF THERE IS AMBIGUOUS NOMENCLATURE // !! (E.G. BOTH OPCODE AND LABEL ARE THE SAME STRING) @@ -18,6 +20,9 @@ pub fn bubble_arg_call( // mis: Parent macro invocations and their indices mis: &mut [(usize, MacroInvocation)], jump_table: &mut JumpTable, + circular_codesize_invocations: &mut CircularCodeSizeIndices, + label_indices: &mut LabelIndices, + table_instances: &mut Jumps, ) -> Result<(), CodegenError> { let starting_offset = *offset; @@ -78,6 +83,9 @@ pub fn bubble_arg_call( offset, &mut mis[..mis_len.saturating_sub(1)], jump_table, + circular_codesize_invocations, + label_indices, + table_instances, ) } else { bubble_arg_call( @@ -89,6 +97,9 @@ pub fn bubble_arg_call( offset, mis, jump_table, + circular_codesize_invocations, + label_indices, + table_instances, ) } } @@ -124,12 +135,66 @@ pub fn bubble_arg_call( kind: CodegenErrorKind::StoragePointersNotDerived, span: AstSpan(vec![]), token: None, - }) + }); } }; *offset += push_bytes.len() / 2; tracing::info!(target: "codegen", "OFFSET: {}, PUSH BYTES: {:?}", offset, push_bytes); bytes.push((starting_offset, Bytes(push_bytes))); + } else if let Some(ir_macro) = contract.find_macro_by_name(iden) { + let new_scope = ir_macro.clone(); + scope.push(new_scope); + tracing::debug!(target: "codegen", "ARG CALL IS MACRO: {}", iden); + tracing::debug!(target: "codegen", "CURRENT MACRO DEF: {}", macro_def.name); + + mis.push(( + *offset, + MacroInvocation { + args: vec![], + span: AstSpan(vec![]), + macro_name: iden.to_string(), + }, + )); + + let mut res: BytecodeRes = match Codegen::macro_to_bytecode( + ir_macro.clone(), + contract, + scope, + *offset, + mis, + false, + Some(circular_codesize_invocations), + ) { + Ok(r) => r, + Err(e) => { + tracing::error!( + target: "codegen", + "FAILED TO RECURSE INTO MACRO \"{}\"", + ir_macro.name + ); + return Err(e); + } + }; + + for j in res.unmatched_jumps.iter_mut() { + let new_index = j.bytecode_index; + j.bytecode_index = 0; + let mut new_jumps = if let Some(jumps) = jump_table.get(&new_index) + { + jumps.clone() + } else { + vec![] + }; + new_jumps.push(j.clone()); + jump_table.insert(new_index, new_jumps); + } + table_instances.extend(res.table_instances); + label_indices.extend(res.label_indices); + + // Increase offset by byte length of recursed macro + *offset += res.bytes.iter().map(|(_, b)| b.0.len()).sum::() / 2; + // Add the macro's bytecode to the final result + res.bytes.iter().for_each(|(a, b)| bytes.push((*a, b.clone()))); } else if let Ok(o) = Opcode::from_str(iden) { tracing::debug!(target: "codegen", "Found Opcode: {}", o); let b = Bytes(o.to_string()); diff --git a/huff_codegen/src/lib.rs b/huff_codegen/src/lib.rs index e281235f..eda9f1cc 100644 --- a/huff_codegen/src/lib.rs +++ b/huff_codegen/src/lib.rs @@ -348,6 +348,9 @@ impl Codegen { &mut offset, mis, &mut jump_table, + circular_codesize_invocations, + &mut label_indices, + &mut table_instances, )? } } From 455ab7e986c8fcd334e9178cd8ab722ad6b05534 Mon Sep 17 00:00:00 2001 From: d1ll0n Date: Thu, 30 Mar 2023 04:16:01 -0500 Subject: [PATCH 3/6] add test for bubbled macro macro args -- currently causes compiler to freeze due to mutex deadlock --- huff_core/tests/macro_invoc_args.rs | 52 +++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/huff_core/tests/macro_invoc_args.rs b/huff_core/tests/macro_invoc_args.rs index aff0d65e..575f0b66 100644 --- a/huff_core/tests/macro_invoc_args.rs +++ b/huff_core/tests/macro_invoc_args.rs @@ -346,3 +346,55 @@ fn test_macro_macro_arg() { // Check the bytecode assert_eq!(main_bytecode.to_lowercase(), expected_bytecode.to_lowercase()); } + +#[test] +fn test_bubbled_macro_macro_arg() { + let source = r#" + #define constant TWO = 0x02 + + #define macro MUL_BY_10() = takes(1) returns (1) { + 0x0a mul + } + + #define macro DO_OP(op) = takes(0) returns(0) { + + } + + #define macro DIV_BY_5() = takes(1) returns (1) { + 0x05 swap1 DO_OP(div) + } + + #define macro EXEC_WITH_VALUE(value, macro) = takes(0) returns(1) { + + } + + #define macro SUM_RESULTS(value, macro1, macro2) = takes(0) returns (1) { + EXEC_WITH_VALUE(, ) + EXEC_WITH_VALUE(, ) + add + } + + #define macro MAIN() = takes(0) returns(0) { + SUM_RESULTS(TWO, MUL_BY_10, DIV_BY_5) + } + "#; + + // Lex + Parse + let flattened_source = FullFileSource { source, file: None, spans: vec![] }; + let lexer = Lexer::new(flattened_source.source); + let tokens = lexer.into_iter().map(|x| x.unwrap()).collect::>(); + let mut parser = Parser::new(tokens, None); + let mut contract = parser.parse().unwrap(); + contract.derive_storage_pointers(); + + let evm_version = EVMVersion::default(); + + // Create main and constructor bytecode + let main_bytecode = Codegen::generate_main_bytecode(&evm_version, &contract, None).unwrap(); + + // Full expected bytecode output (generated from huffc) (placed here as a reference) + let expected_bytecode = "6002600a0260026005900401"; + + // Check the bytecode + assert_eq!(main_bytecode.to_lowercase(), expected_bytecode.to_lowercase()); +} From 16e19f24d86c816f40c4caaff9394bbb835f306a Mon Sep 17 00:00:00 2001 From: d1ll0n Date: Thu, 30 Mar 2023 04:18:55 -0500 Subject: [PATCH 4/6] change order of checks for MacroArg::Ident - this seems to fix the mutex deadlock in the constants lookup --- huff_codegen/src/irgen/arg_calls.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/huff_codegen/src/irgen/arg_calls.rs b/huff_codegen/src/irgen/arg_calls.rs index 88e928e3..89eeb070 100644 --- a/huff_codegen/src/irgen/arg_calls.rs +++ b/huff_codegen/src/irgen/arg_calls.rs @@ -107,7 +107,12 @@ pub fn bubble_arg_call( tracing::debug!(target: "codegen", "Found MacroArg::Ident IN \"{}\" Macro Invocation: \"{}\"!", macro_invoc.1.macro_name, iden); // Check for a constant first - if let Some(constant) = contract + if let Ok(o) = Opcode::from_str(iden) { + tracing::debug!(target: "codegen", "Found Opcode: {}", o); + let b = Bytes(o.to_string()); + *offset += b.0.len() / 2; + bytes.push((starting_offset, b)); + } else if let Some(constant) = contract .constants .lock() .map_err(|_| { @@ -195,11 +200,6 @@ pub fn bubble_arg_call( *offset += res.bytes.iter().map(|(_, b)| b.0.len()).sum::() / 2; // Add the macro's bytecode to the final result res.bytes.iter().for_each(|(a, b)| bytes.push((*a, b.clone()))); - } else if let Ok(o) = Opcode::from_str(iden) { - tracing::debug!(target: "codegen", "Found Opcode: {}", o); - let b = Bytes(o.to_string()); - *offset += b.0.len() / 2; - bytes.push((starting_offset, b)); } else { tracing::debug!(target: "codegen", "Found Label Call: {}", iden); From 7a0d7671cd82f34e0e748b11395c50f062c6dfdc Mon Sep 17 00:00:00 2001 From: d1ll0n Date: Fri, 31 Mar 2023 16:16:25 -0500 Subject: [PATCH 5/6] Fix comment about constants --- huff_codegen/src/irgen/arg_calls.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/huff_codegen/src/irgen/arg_calls.rs b/huff_codegen/src/irgen/arg_calls.rs index 89eeb070..63aff581 100644 --- a/huff_codegen/src/irgen/arg_calls.rs +++ b/huff_codegen/src/irgen/arg_calls.rs @@ -106,7 +106,9 @@ pub fn bubble_arg_call( MacroArg::Ident(iden) => { tracing::debug!(target: "codegen", "Found MacroArg::Ident IN \"{}\" Macro Invocation: \"{}\"!", macro_invoc.1.macro_name, iden); - // Check for a constant first + // The opcode check needs to happens before the constants lookup + // because otherwise the mutex can deadlock when bubbling up to + // resolve macros as arguments. if let Ok(o) = Opcode::from_str(iden) { tracing::debug!(target: "codegen", "Found Opcode: {}", o); let b = Bytes(o.to_string()); From aff06fcd4b0f37ddb132e6f57ce87902ae5a724e Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Sun, 1 Oct 2023 17:03:21 +0000 Subject: [PATCH 6/6] fix: rebase dils work on stage --- huff_codegen/src/irgen/arg_calls.rs | 31 +++++++++++++++++------------ huff_codegen/src/lib.rs | 1 + 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/huff_codegen/src/irgen/arg_calls.rs b/huff_codegen/src/irgen/arg_calls.rs index 63aff581..2d3343f4 100644 --- a/huff_codegen/src/irgen/arg_calls.rs +++ b/huff_codegen/src/irgen/arg_calls.rs @@ -11,6 +11,7 @@ use crate::Codegen; /// Arg Call Bubbling #[allow(clippy::too_many_arguments)] pub fn bubble_arg_call( + evm_version: &EVMVersion, arg_name: &str, bytes: &mut Vec<(usize, Bytes)>, macro_def: &MacroDefinition, @@ -75,6 +76,7 @@ pub fn bubble_arg_call( let ac_ = &ac.to_string(); return if last_mi.1.macro_name.eq(¯o_def.name) { bubble_arg_call( + evm_version, ac_, bytes, bubbled_macro_invocation, @@ -89,6 +91,7 @@ pub fn bubble_arg_call( ) } else { bubble_arg_call( + evm_version, ac_, bytes, bubbled_macro_invocation, @@ -110,10 +113,10 @@ pub fn bubble_arg_call( // because otherwise the mutex can deadlock when bubbling up to // resolve macros as arguments. if let Ok(o) = Opcode::from_str(iden) { - tracing::debug!(target: "codegen", "Found Opcode: {}", o); - let b = Bytes(o.to_string()); - *offset += b.0.len() / 2; - bytes.push((starting_offset, b)); + tracing::debug!(target: "codegen", "Found Opcode: {}", o); + let b = Bytes(o.to_string()); + *offset += b.0.len() / 2; + bytes.push((starting_offset, b)); } else if let Some(constant) = contract .constants .lock() @@ -142,33 +145,35 @@ pub fn bubble_arg_call( kind: CodegenErrorKind::StoragePointersNotDerived, span: AstSpan(vec![]), token: None, - }); + }) } }; *offset += push_bytes.len() / 2; tracing::info!(target: "codegen", "OFFSET: {}, PUSH BYTES: {:?}", offset, push_bytes); bytes.push((starting_offset, Bytes(push_bytes))); } else if let Some(ir_macro) = contract.find_macro_by_name(iden) { - let new_scope = ir_macro.clone(); - scope.push(new_scope); tracing::debug!(target: "codegen", "ARG CALL IS MACRO: {}", iden); tracing::debug!(target: "codegen", "CURRENT MACRO DEF: {}", macro_def.name); - mis.push(( + let mut new_scopes = scope.to_vec(); + new_scopes.push(ir_macro); + let mut new_mis = mis.to_vec(); + new_mis.push(( *offset, MacroInvocation { + macro_name: iden.to_string(), args: vec![], span: AstSpan(vec![]), - macro_name: iden.to_string(), }, )); let mut res: BytecodeRes = match Codegen::macro_to_bytecode( - ir_macro.clone(), + evm_version, + ir_macro, contract, - scope, + &mut new_scopes, *offset, - mis, + &mut new_mis, false, Some(circular_codesize_invocations), ) { @@ -179,7 +184,7 @@ pub fn bubble_arg_call( "FAILED TO RECURSE INTO MACRO \"{}\"", ir_macro.name ); - return Err(e); + return Err(e) } }; diff --git a/huff_codegen/src/lib.rs b/huff_codegen/src/lib.rs index eda9f1cc..84657b14 100644 --- a/huff_codegen/src/lib.rs +++ b/huff_codegen/src/lib.rs @@ -340,6 +340,7 @@ impl Codegen { // Bubble up arg call by looking through the previous scopes. // Once the arg value is found, add it to `bytes` bubble_arg_call( + evm_version, arg_name, &mut bytes, macro_def,