From dca4f367b39beb21a519fc8ebf63e59d2e72eeaa Mon Sep 17 00:00:00 2001 From: d1ll0n Date: Thu, 30 Mar 2023 03:43:04 -0500 Subject: [PATCH 1/7] Add test for bubbled macro args with different names --- huff_core/tests/macro_invoc_args.rs | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/huff_core/tests/macro_invoc_args.rs b/huff_core/tests/macro_invoc_args.rs index 0b498432..6d695934 100644 --- a/huff_core/tests/macro_invoc_args.rs +++ b/huff_core/tests/macro_invoc_args.rs @@ -260,3 +260,37 @@ fn test_bubbled_constant_macro_arg() { // Check the bytecode assert_eq!(bytecode.to_lowercase(), expected_bytecode.to_lowercase()); } + + +#[test] +fn test_bubbled_arg_with_different_name() { + let source = r#" + #define macro MACRO_A(arg_a) = takes(0) returns(0) { + + } + #define macro MACRO_B(arg_b) =takes(0) returns(0) { + MACRO_A() + } + #define macro MAIN() = takes(0) returns(0){ + MACRO_B(0x01) + } + "#; + + // Lex + Parse + let flattened_source = FullFileSource { source, file: None, spans: vec![] }; + let lexer = Lexer::new(flattened_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(); + + // Create main and constructor bytecode + let main_bytecode = Codegen::generate_main_bytecode(&contract, None).unwrap(); + + // Full expected bytecode output (generated from huffc) (placed here as a reference) + let expected_bytecode = "6001"; + + // Check the bytecode + assert_eq!(main_bytecode, expected_bytecode); + +} \ No newline at end of file From a5e4768776a96ad4023231119e35f0091e109e44 Mon Sep 17 00:00:00 2001 From: d1ll0n Date: Thu, 30 Mar 2023 03:45:21 -0500 Subject: [PATCH 2/7] use argcall for bubbled resolution instead of original name --- huff_codegen/src/irgen/arg_calls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/huff_codegen/src/irgen/arg_calls.rs b/huff_codegen/src/irgen/arg_calls.rs index 58ccbe51..47e98899 100644 --- a/huff_codegen/src/irgen/arg_calls.rs +++ b/huff_codegen/src/irgen/arg_calls.rs @@ -67,7 +67,7 @@ pub fn bubble_arg_call( }; return if last_mi.1.macro_name.eq(¯o_def.name) { bubble_arg_call( - arg_name, + ac, bytes, &bubbled_macro_invocation, contract, @@ -78,7 +78,7 @@ pub fn bubble_arg_call( ) } else { bubble_arg_call( - arg_name, + &ac.to_string(), bytes, &bubbled_macro_invocation, contract, From da1fafca7e325803778aa74708d6741886081f2c Mon Sep 17 00:00:00 2001 From: d1ll0n Date: Thu, 30 Mar 2023 04:12:26 -0500 Subject: [PATCH 3/7] add test for basic macro macro arg --- huff_core/tests/macro_invoc_args.rs | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/huff_core/tests/macro_invoc_args.rs b/huff_core/tests/macro_invoc_args.rs index 6d695934..5a99f866 100644 --- a/huff_core/tests/macro_invoc_args.rs +++ b/huff_core/tests/macro_invoc_args.rs @@ -293,4 +293,40 @@ 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); + 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(); + + // Create main and constructor bytecode + let main_bytecode = Codegen::generate_main_bytecode(&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()); } \ No newline at end of file From e9eae5366e1880e498ad3cb28b947982c2e5befd Mon Sep 17 00:00:00 2001 From: d1ll0n Date: Thu, 30 Mar 2023 04:14:21 -0500 Subject: [PATCH 4/7] 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 47e98899..e97e65a6 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 Vec<(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; @@ -75,6 +80,9 @@ pub fn bubble_arg_call( offset, &mut Vec::from(&mis[..mis.len().saturating_sub(1)]), jump_table, + circular_codesize_invocations, + label_indices, + table_instances, ) } else { bubble_arg_call( @@ -86,6 +94,9 @@ pub fn bubble_arg_call( offset, mis, jump_table, + circular_codesize_invocations, + label_indices, + table_instances, ) } } @@ -121,12 +132,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 3c77f813..bd980a61 100644 --- a/huff_codegen/src/lib.rs +++ b/huff_codegen/src/lib.rs @@ -341,6 +341,9 @@ impl Codegen { &mut offset, mis, &mut jump_table, + circular_codesize_invocations, + &mut label_indices, + &mut table_instances, )? } } From e25e78c1ad1aa237b36ef2a6a3344ab23275c221 Mon Sep 17 00:00:00 2001 From: d1ll0n Date: Thu, 30 Mar 2023 04:16:01 -0500 Subject: [PATCH 5/7] 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, 51 insertions(+), 1 deletion(-) diff --git a/huff_core/tests/macro_invoc_args.rs b/huff_core/tests/macro_invoc_args.rs index 5a99f866..dbdb1562 100644 --- a/huff_core/tests/macro_invoc_args.rs +++ b/huff_core/tests/macro_invoc_args.rs @@ -329,4 +329,54 @@ fn test_macro_macro_arg() { // Check the bytecode assert_eq!(main_bytecode.to_lowercase(), expected_bytecode.to_lowercase()); -} \ No newline at end of file +} + +#[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); + 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(); + + // Create main and constructor bytecode + let main_bytecode = Codegen::generate_main_bytecode(&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 de3834674a50d481b88acca39d6403bd014f0447 Mon Sep 17 00:00:00 2001 From: d1ll0n Date: Thu, 30 Mar 2023 04:18:55 -0500 Subject: [PATCH 6/7] 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 e97e65a6..901b9536 100644 --- a/huff_codegen/src/irgen/arg_calls.rs +++ b/huff_codegen/src/irgen/arg_calls.rs @@ -104,7 +104,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(|_| { @@ -192,11 +197,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 ee5e7641fc6f22bd75c115356644557487dae926 Mon Sep 17 00:00:00 2001 From: d1ll0n Date: Fri, 31 Mar 2023 16:16:25 -0500 Subject: [PATCH 7/7] 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 901b9536..e4ac4da3 100644 --- a/huff_codegen/src/irgen/arg_calls.rs +++ b/huff_codegen/src/irgen/arg_calls.rs @@ -103,7 +103,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());