Repository: huff-language/huff-rs
Pull request #167 (2022-07-12 23:08:45)

stage -> main
### `stage` -> `main`

### Changelog

- Fix Missing Codegen Errors in Constructor by @abigger87 in #166 
- Fix Code Tables by @clabby in #168 
- Feat: Macro Outlining by @clabby in #164 
- Feat: Constant Overrides by @clabby in #171 


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 8841e70..41c65e3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -718,6 +718,7 @@ dependencies = [
  "huff_lexer",
  "huff_utils",
  "proptest",
+ "regex",
  "tiny-keccak",
  "tracing",
 ]
@@ -1279,9 +1280,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.5.6"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
+checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1299,9 +1300,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.26"
+version = "0.6.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
+checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
 
 [[package]]
 name = "remove_dir_all"
diff --git a/huff_cli/src/huffc.rs b/huff_cli/src/huffc.rs
index c20a643..cf9a596 100644
--- a/huff_cli/src/huffc.rs
+++ b/huff_cli/src/huffc.rs
@@ -12,12 +12,12 @@ use ethers_core::utils::hex;
 use huff_codegen::Codegen;
 use huff_core::Compiler;
 use huff_utils::prelude::{
-    export_interfaces, gen_sol_interfaces, unpack_files, AstSpan, CodegenError, CodegenErrorKind,
-    CompilerError, FileSource, OutputLocation, Span,
+    export_interfaces, gen_sol_interfaces, str_to_bytes32, unpack_files, AstSpan, CodegenError,
+    CodegenErrorKind, CompilerError, FileSource, Literal, OutputLocation, Span,
 };
 use isatty::stdout_isatty;
 use spinners::{Spinner, Spinners};
-use std::{io::Write, path::Path, sync::Arc};
+use std::{collections::BTreeMap, io::Write, path::Path, sync::Arc};
 use yansi::Paint;
 
 /// The Huff CLI Args
@@ -70,6 +70,10 @@ struct Huff {
     /// Verbose output.
     #[clap(short = 'v', long = "verbose")]
     verbose: bool,
+
+    /// Override / set constants for the compilation environment.
+    #[clap(short = 'c', long = "constants", multiple_values = true)]
+    constants: Option<Vec<String>>,
 }
 
 /// Helper function to read an stdin input
@@ -102,6 +106,33 @@ fn main() {
         }
     };
 
+    // If constant overrides were passed, create a map of their names and values
+    let constants: Option<BTreeMap<&str, Literal>> = cli.constants.as_ref().map(|_constants| {
+        _constants
+            .iter()
+            .map(|c: &String| {
+                let parts = c.as_str().split('=').collect::<Vec<_>>();
+
+                // Check that constant override argument is valid
+                // Key rule: Alphabetic chars + underscore
+                // Value rule: Valid literal string (0x...)
+                if parts.len() != 2 ||
+                    parts[0].chars().any(|c| !(c.is_alphabetic() || c == '_')) ||
+                    !parts[1].starts_with("0x") ||
+                    parts[1][2..].chars().any(|c| {
+                        !(c.is_numeric() ||
+                            matches!(c, '\u{0041}'..='\u{0046}' | '\u{0061}'..='\u{0066}'))
+                    })
+                {
+                    eprintln!("Invalid constant override argument: {}", Paint::red(c.to_string()));
+                    std::process::exit(1);
+                }
+
+                (parts[0], str_to_bytes32(&parts[1][2..]))
+            })
+            .collect()
+    });
+
     let mut use_cache = true;
     if cli.interactive {
         // Don't accept configured inputs
@@ -124,6 +155,7 @@ fn main() {
         sources: Arc::clone(&sources),
         output,
         construct_args: cli.inputs,
+        constant_overrides: constants,
         optimize: cli.optimize,
         bytecode: cli.bytecode,
         cached: use_cache,
diff --git a/huff_codegen/README.md b/huff_codegen/README.md
index cc9ad30..4a63122 100644
--- a/huff_codegen/README.md
+++ b/huff_codegen/README.md
@@ -27,6 +27,8 @@ Below we showcase generating a compile artifact from compiled bytecode using `hu
 use huff_codegen::*;
 use huff_utils::files::FileSource;
 use std::sync::Arc;
+use std::cell::RefCell;
+use std::rc::Rc;
 
 // Instantiate an empty Codegen
 let mut cg = Codegen::new();
@@ -53,6 +55,8 @@ Let's say you have a [Contract](../huff_utils/ast/struct.Contract.html) instance
 use huff_codegen::*;
 use huff_utils::prelude::*;
 use std::sync::Arc;
+use std::cell::RefCell;
+use std::rc::Rc;
 
 // Mock contract with a main macro
 let contract = Contract {
@@ -81,11 +85,12 @@ let contract = Contract {
       takes: 0,
       returns: 0,
       span: AstSpan(vec![]),
+      outlined: false,
     }
   ],
   invocations: vec![],
   imports: vec![],
-  constants: vec![],
+  constants: Rc::new(RefCell::new(vec![])),
   functions: vec![],
   events: vec![],
   tables: vec![],
@@ -104,6 +109,8 @@ Similarly, once you have a [Contract](../huff_utils/ast/struct.Contract.html) in
 use huff_codegen::*;
 use huff_utils::prelude::*;
 use std::sync::Arc;
+use std::cell::RefCell;
+use std::rc::Rc;
 
 // Mock contract with a constructor macro
 let contract = Contract {
@@ -132,11 +139,12 @@ let contract = Contract {
       takes: 0,
       returns: 0,
       span: AstSpan(vec![]),
+      outlined: false,
     }
   ],
   invocations: vec![],
   imports: vec![],
-  constants: vec![],
+  constants: Rc::new(RefCell::new(vec![])),
   functions: vec![],
   events: vec![],
   tables: vec![],
diff --git a/huff_codegen/src/irgen/arg_calls.rs b/huff_codegen/src/irgen/arg_calls.rs
index 6e62d55..f1bc6f5 100644
--- a/huff_codegen/src/irgen/arg_calls.rs
+++ b/huff_codegen/src/irgen/arg_calls.rs
@@ -22,7 +22,8 @@ pub fn bubble_arg_call(
     let starting_offset = *offset;
 
     // Check Constant Definitions
-    if let Some(constant) = contract.constants.iter().find(|const_def| const_def.name.eq(arg_name))
+    if let Some(constant) =
+        contract.constants.borrow().iter().find(|const_def| const_def.name.eq(arg_name))
     {
         tracing::info!(target: "codegen", "ARGCALL IS CONSTANT: {:?}", constant);
         let push_bytes = match &constant.value {
diff --git a/huff_codegen/src/irgen/constants.rs b/huff_codegen/src/irgen/constants.rs
index a4a68c4..ab0bfc3 100644
--- a/huff_codegen/src/irgen/constants.rs
+++ b/huff_codegen/src/irgen/constants.rs
@@ -9,18 +9,18 @@ pub fn constant_gen(
     ir_byte_span: AstSpan,
 ) -> Result<String, CodegenError> {
     // Get the first `ConstantDefinition` that matches the constant's name
-    let constant =
-        if let Some(m) = contract.constants.iter().find(|const_def| const_def.name.eq(&name)) {
-            m
-        } else {
-            tracing::error!(target: "codegen", "MISSING CONSTANT DEFINITION \"{}\"", name);
+    let constants = contract.constants.borrow();
+    let constant = if let Some(m) = constants.iter().find(|const_def| const_def.name.eq(&name)) {
+        m
+    } else {
+        tracing::error!(target: "codegen", "MISSING CONSTANT DEFINITION \"{}\"", name);
 
-            return Err(CodegenError {
-                kind: CodegenErrorKind::MissingConstantDefinition(name.to_string()),
-                span: ir_byte_span,
-                token: None,
-            })
-        };
+        return Err(CodegenError {
+            kind: CodegenErrorKind::MissingConstantDefinition(name.to_string()),
+            span: ir_byte_span,
+            token: None,
+        })
+    };
 
     // Generate bytecode for the constant
     // Should always be a `Literal` if storage pointers were derived in the AST
diff --git a/huff_codegen/src/irgen/statements.rs b/huff_codegen/src/irgen/statements.rs
index 21a881b..6e85671 100644
--- a/huff_codegen/src/irgen/statements.rs
+++ b/huff_codegen/src/irgen/statements.rs
@@ -15,6 +15,7 @@ pub fn statement_gen(
     jump_table: &mut JumpTable,
     label_indices: &mut LabelIndices,
     table_instances: &mut Jumps,
+    utilized_tables: &mut Vec<TableDefinition>,
     starting_offset: usize,
 ) -> Result<Vec<(usize, Bytes)>, CodegenError> {
     let mut bytes = vec![];
@@ -41,12 +42,57 @@ pub fn statement_gen(
 
             tracing::info!(target: "codegen", "FOUND INNER MACRO: {}", ir_macro.name);
 
-            // Recurse into macro invocation
-            scope.push(ir_macro.clone());
-            mis.push((*offset, mi.clone()));
+            // If invoked macro is a function (outlined), insert a jump to the function's code and a
+            // jumpdest to return to. If it is inlined, insert the macro's code at the
+            // current offset.
+            if ir_macro.outlined {
+                // Get necessary swap ops to reorder stack
+                // PC of the return jumpdest should be below the function's stack inputs
+                let stack_swaps = (0..ir_macro.takes)
+                    .rev()
+                    .map(|i| format!("{:02x}", 0x90 + i))
+                    .collect::<Vec<_>>();
 
-            let mut res: BytecodeRes =
-                match Codegen::macro_to_bytecode(ir_macro.clone(), contract, scope, *offset, mis) {
+                // Insert a jump to the outlined macro's code
+                jump_table.insert(
+                    *offset + stack_swaps.len() + 3, // PUSH2 + 2 bytes + stack_swaps.len()
+                    vec![Jump {
+                        label: format!("goto_{}", &ir_macro.name),
+                        bytecode_index: 0,
+                        span: s.span.clone(),
+                    }],
+                );
+
+                // Store return JUMPDEST PC on the stack and re-order the stack so that
+                // the return JUMPDEST PC is below the function's stack inputs
+                bytes.push((
+                    *offset,
+                    Bytes(format!(
+                        "{}{:04x}{}",
+                        Opcode::Push2,
+                        *offset + stack_swaps.len() + 7,
+                        stack_swaps.join("")
+                    )),
+                ));
+                // Insert jump to outlined macro + jumpdest to return to
+                bytes.push((
+                    *offset + stack_swaps.len() + 3, // PUSH2 + 2 bytes + stack_swaps.len()
+                    Bytes(format!("{}xxxx{}{}", Opcode::Push2, Opcode::Jump, Opcode::Jumpdest)),
+                ));
+                // PUSH2 + 2 bytes + stack_swaps.len() + PUSH2 + 2 bytes + JUMP + JUMPDEST
+                *offset += stack_swaps.len() + 8;
+            } else {
+                // Recurse into macro invocation
+                scope.push(ir_macro.clone());
+                mis.push((*offset, mi.clone()));
+
+                let mut res: BytecodeRes = match Codegen::macro_to_bytecode(
+                    ir_macro.clone(),
+                    contract,
+                    scope,
+                    *offset,
+                    mis,
+                ) {
                     Ok(r) => r,
                     Err(e) => {
                         tracing::error!(
@@ -58,26 +104,28 @@ pub fn statement_gen(
                     }
                 };
 
-            // Set jump table values
-            tracing::debug!(target: "codegen", "Unmatched jumps: {:?}", res.unmatched_jumps.iter().map(|uj| uj.label.clone()).collect::<Vec<String>>());
-            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);
+                // Set jump table values
+                tracing::debug!(target: "codegen", "Unmatched jumps: {:?}", res.unmatched_jumps.iter().map(|uj| uj.label.clone()).collect::<Vec<String>>());
+                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);
+                utilized_tables.extend(res.utilized_tables);
 
-            // Increase offset by byte length of recursed macro
-            *offset += res.bytes.iter().map(|(_, b)| b.0.len()).sum::<usize>() / 2;
-            // Add the macro's bytecode to the final result
-            bytes = [bytes, res.bytes].concat()
+                // Increase offset by byte length of recursed macro
+                *offset += res.bytes.iter().map(|(_, b)| b.0.len()).sum::<usize>() / 2;
+                // Add the macro's bytecode to the final result
+                bytes = [bytes, res.bytes].concat()
+            }
         }
         StatementType::Label(label) => {
             // Add JUMPDEST opcode to final result and add to label_indices
@@ -88,8 +136,7 @@ pub fn statement_gen(
         }
         StatementType::LabelCall(label) => {
             // Generate code for a `LabelCall`
-            // PUSH2 + 2 byte destination (placeholder for now, filled at the bottom
-            // of this function)
+            // PUSH2 + 2 byte destination (placeholder for now, filled in `Codegen::fill_unmatched`
             tracing::info!(target: "codegen", "RECURSE BYTECODE GOT LABEL CALL: {}", label);
             jump_table.insert(
                 *offset,
@@ -162,7 +209,7 @@ pub fn statement_gen(
                             bf.args[0].name.as_ref().unwrap()
                         );
                         return Err(CodegenError {
-                            kind: CodegenErrorKind::MissingMacroDefinition(
+                            kind: CodegenErrorKind::InvalidMacroInvocation(
                                 bf.args[0].name.as_ref().unwrap().to_string(), /* yuck */
                             ),
                             span: bf.span.clone(),
@@ -173,18 +220,42 @@ pub fn statement_gen(
                     let size = bytes32_to_string(&ir_table.size, false);
                     let push_bytes = format!("{:02x}{}", 95 + size.len() / 2, size);
 
+                    if !utilized_tables.contains(&ir_table) {
+                        utilized_tables.push(ir_table);
+                    }
+
                     *offset += push_bytes.len() / 2;
                     bytes.push((starting_offset, Bytes(push_bytes)));
                 }
                 BuiltinFunctionKind::Tablestart => {
-                    table_instances.push(Jump {
-                        label: bf.args[0].name.as_ref().unwrap().to_owned(),
-                        bytecode_index: *offset,
-                        span: bf.span.clone(),
-                    });
+                    // Make sure the table exists
+                    if let Some(t) = contract.find_table_by_name(bf.args[0].name.as_ref().unwrap())
+                    {
+                        table_instances.push(Jump {
+                            label: bf.args[0].name.as_ref().unwrap().to_owned(),
+                            bytecode_index: *offset,
+                            span: bf.span.clone(),
+                        });
+                        if !utilized_tables.contains(&t) {
+                            utilized_tables.push(t);
+                        }
 
-                    bytes.push((*offset, Bytes(format!("{}xxxx", Opcode::Push2))));
-                    *offset += 3;
+                        bytes.push((*offset, Bytes(format!("{}xxxx", Opcode::Push2))));
+                        *offset += 3;
+                    } else {
+                        tracing::error!(
+                            target: "codegen",
+                            "MISSING TABLE PASSED TO __tablestart \"{}\"",
+                            bf.args[0].name.as_ref().unwrap()
+                        );
+                        return Err(CodegenError {
+                            kind: CodegenErrorKind::InvalidMacroInvocation(
+                                bf.args[0].name.as_ref().unwrap().to_string(),
+                            ),
+                            span: bf.span.clone(),
+                            token: None,
+                        })
+                    }
                 }
                 BuiltinFunctionKind::FunctionSignature => {
                     if bf.args.len() != 1 {
diff --git a/huff_codegen/src/lib.rs b/huff_codegen/src/lib.rs
index e633fe8..f93131d 100644
--- a/huff_codegen/src/lib.rs
+++ b/huff_codegen/src/lib.rs
@@ -10,6 +10,7 @@ use huff_utils::{
     ast::*,
     bytecode::*,
     error::CodegenError,
+    evm::Opcode,
     prelude::{
         bytes32_to_string, format_even_bytes, pad_n_bytes, CodegenErrorKind, FileSource, Span,
     },
@@ -68,7 +69,7 @@ impl Codegen {
         )?;
 
         // Generate the fully baked bytecode
-        Codegen::gen_table_bytecode(bytecode_res, contract)
+        Codegen::gen_table_bytecode(bytecode_res)
     }
 
     /// Generates constructor bytecode from a Contract AST
@@ -85,8 +86,7 @@ impl Codegen {
             &mut Vec::default(),
         )?;
 
-        // Generate the bytecode return string
-        Codegen::gen_table_bytecode(bytecode_res, contract)
+        Codegen::gen_table_bytecode(bytecode_res)
     }
 
     /// Helper function to find a macro or generate a CodegenError
@@ -108,10 +108,7 @@ impl Codegen {
 
     /// Appends table bytecode to the end of the BytecodeRes output.
     /// Fills table JUMPDEST placeholders.
-    pub(crate) fn gen_table_bytecode(
-        res: BytecodeRes,
-        contract: &Contract,
-    ) -> Result<String, CodegenError> {
+    pub(crate) fn gen_table_bytecode(res: BytecodeRes) -> Result<String, CodegenError> {
         if !res.unmatched_jumps.is_empty() {
             tracing::error!(
                 target: "codegen",
@@ -136,9 +133,9 @@ impl Codegen {
         let mut table_offsets: HashMap<String, usize> = HashMap::new(); // table name -> bytecode offset
         let mut table_offset = bytecode.len() / 2;
 
-        if let Err(e) = contract.tables.iter().try_for_each(|jt| {
+        res.utilized_tables.iter().try_for_each(|jt| {
             table_offsets.insert(jt.name.to_string(), table_offset);
-            let size = match bytes32_to_string(&jt.size, false).parse::<usize>() {
+            let size = match bytes32_to_string(&jt.size, false).as_str().parse::<usize>() {
                 Ok(s) => s,
                 Err(_) => return Err(CodegenError {
                     kind: CodegenErrorKind::UsizeConversion(format!("{:?}", jt.size)),
@@ -155,40 +152,57 @@ impl Codegen {
                 .statements
                 .iter()
                 .try_for_each(|s| {
-                    if let StatementType::LabelCall(label) = &s.ty {
-                        let offset = match res.label_indices.get(label) {
-                            Some(l) => l,
-                            None => {
-                                tracing::error!(
+                    match &s.ty {
+                        StatementType::LabelCall(label) => {
+                            let offset = match res.label_indices.get(label) {
+                                Some(l) => l,
+                                None => {
+                                    tracing::error!(
                                     target: "codegen",
                                     "Definition not found for Jump Table Label: \"{}\"",
                                     label
                                 );
+                                    return Err(CodegenError {
+                                        kind: CodegenErrorKind::UnmatchedJumpLabel,
+                                        span: s.span.clone(),
+                                        token: None,
+                                    });
+                                }
+                            };
+                            let hex = format_even_bytes(format!("{:02x}", offset));
+
+                            table_code = format!("{}{}", table_code, pad_n_bytes(
+                                hex.as_str(),
+                                if matches!(jt.kind, TableKind::JumpTablePacked) { 0x02 } else { 0x20 },
+                            ));
+                        }
+                        StatementType::Code(code) => {
+                            // Check if code length is even
+                            if code.len() % 2 != 0 {
                                 return Err(CodegenError {
-                                    kind: CodegenErrorKind::UnmatchedJumpLabel,
+                                    kind: CodegenErrorKind::InvalidCodeLength(code.len()),
                                     span: s.span.clone(),
                                     token: None,
                                 });
                             }
-                        };
-                        let hex = format_even_bytes(format!("{:02x}", offset));
 
-                        table_code = format!("{}{}", table_code, pad_n_bytes(
-                            hex.as_str(),
-                            if matches!(jt.kind, TableKind::JumpTablePacked) { 0x02 } else { 0x20 },
-                        ));
+                            table_code = format!("{}{}", table_code, code);
+                        }
+                        _ => {
+                            return Err(CodegenError {
+                                kind: CodegenErrorKind::InvalidMacroStatement,
+                                span: jt.span.clone(),
+                                token: None
+                            })
+                        }
                     }
                     Ok(())
                 });
-            if let Err(e) = collected {
-                return Err(e);
-            }
+            collected?;
             tracing::info!(target: "codegen", "SUCCESSFULLY GENERATED BYTECODE FOR TABLE: \"{}\"", jt.name);
             bytecode = format!("{}{}", bytecode, table_code);
             Ok(())
-        }) {
-            return Err(e);
-        }
+        })?;
 
         res.table_instances.iter().for_each(|jump| {
             if let Some(o) = table_offsets.get(&jump.label) {
@@ -245,6 +259,7 @@ impl Codegen {
         let mut jump_table = JumpTable::new();
         let mut label_indices = LabelIndices::new();
         let mut table_instances = Jumps::new();
+        let mut utilized_tables: Vec<TableDefinition> = Vec::new();
 
         // Loop through all intermediate bytecode representations generated from the AST
         for (_ir_bytes_index, ir_byte) in ir_bytes.into_iter().enumerate() {
@@ -271,6 +286,7 @@ impl Codegen {
                         &mut jump_table,
                         &mut label_indices,
                         &mut table_instances,
+                        &mut utilized_tables,
                         starting_offset,
                     )?;
                     bytes.append(&mut push_bytes);
@@ -297,13 +313,29 @@ impl Codegen {
             tracing::warn!(target: "codegen", "ATTEMPTED MACRO INVOCATION POP FAILED AT SCOPE: {}", scope.len());
         }
 
-        let bytecode: String = bytes.iter().map(|byte| byte.0.to_string()).collect();
-        tracing::info!(target: "codegen", "MACRO \"{}\" GENERATED BYTECODE EXCLUDING JUMPS: {}", macro_def.name, bytecode);
+        // Add functions (outlined macros) to the end of the bytecode if the scope length == 1
+        // (i.e., we're at the top level of recursion)
+        if scope.len() == 1 {
+            bytes = Codegen::append_functions(
+                contract,
+                scope,
+                &mut offset,
+                mis,
+                &mut jump_table,
+                &mut label_indices,
+                &mut table_instances,
+                bytes,
+            )?;
+        } else {
+            // If the scope length is > 1, we're processing a child macro. Since we're done
+            // with it, it can be popped.
+            scope.pop();
+        }
 
         // Fill JUMPDEST placeholders
         let (bytes, unmatched_jumps) = Codegen::fill_unmatched(bytes, &jump_table, &label_indices)?;
 
-        Ok(BytecodeRes { bytes, label_indices, unmatched_jumps, table_instances })
+        Ok(BytecodeRes { bytes, label_indices, unmatched_jumps, table_instances, utilized_tables })
     }
 
     /// Helper associated function to fill unmatched jump dests.
@@ -373,6 +405,70 @@ impl Codegen {
         Ok((bytes, unmatched_jumps))
     }
 
+    /// Helper associated function to append functions to the end of the bytecode.
+    ///
+    /// ## Overview
+    ///
+    /// Iterates over the contract's functions, generates their bytecode, fills unmatched jumps &
+    /// label indices, and appends the functions' bytecode to the end of the contract's bytecode.
+    ///
+    /// On success, passes ownership of `bytes` back to the caller.
+    /// On failure, returns a CodegenError.
+    #[allow(clippy::too_many_arguments)]
+    pub fn append_functions(
+        contract: &Contract,
+        scope: &mut Vec<MacroDefinition>,
+        offset: &mut usize,
+        mis: &mut Vec<(usize, MacroInvocation)>,
+        jump_table: &mut JumpTable,
+        label_indices: &mut LabelIndices,
+        table_instances: &mut Jumps,
+        mut bytes: Vec<(usize, Bytes)>,
+    ) -> Result<Vec<(usize, Bytes)>, CodegenError> {
+        for macro_def in contract.macros.iter().filter(|m| m.outlined) {
+            // Push the function to the scope
+            scope.push(macro_def.clone());
+
+            // Add 1 to starting offset to account for the JUMPDEST opcode
+            let mut res =
+                Codegen::macro_to_bytecode(macro_def.clone(), contract, scope, *offset + 1, mis)?;
+
+            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);
+
+            let macro_code_len = res.bytes.iter().map(|(_, b)| b.0.len()).sum::<usize>() / 2;
+
+            // Get necessary swap ops to reorder stack
+            // PC of the return jumpdest should be above the function's outputs on the stack
+            let stack_swaps =
+                (0..macro_def.returns).map(|i| format!("{:02x}", 0x90 + i)).collect::<Vec<_>>();
+
+            // Insert JUMPDEST, stack swaps, and final JUMP back to the location of invocation.
+            bytes.push((*offset, Bytes(Opcode::Jumpdest.to_string())));
+            res.bytes.push((
+                *offset + macro_code_len + 1,
+                Bytes(format!("{}{}", stack_swaps.join(""), Opcode::Jump)),
+            ));
+            bytes = [bytes, res.bytes].concat();
+            // Add the jumpdest to the beginning of the outlined macro.
+            label_indices.insert(format!("goto_{}", macro_def.name.clone()), *offset);
+            *offset += macro_code_len + stack_swaps.len() + 2; // JUMPDEST + MACRO_CODE_LEN +
+                                                               // stack_swaps.len() + JUMP
+        }
+        Ok(bytes)
+    }
+
     /// Generate a codegen artifact
     ///
     /// # Arguments
@@ -518,10 +614,8 @@ impl Codegen {
 
         // If an output's specified, write the artifact out
         if let Some(o) = output {
-            if let Err(e) = Codegen::export(o, art) {
-                // Error message is sent to tracing in `export` if an error occurs
-                return Err(e)
-            }
+            // Error message is sent to tracing in `export` if an error occurs
+            Codegen::export(o, art)?;
         }
 
         // Return the abi
diff --git a/huff_core/README.md b/huff_core/README.md
index 880cbb3..de17c6a 100644
--- a/huff_core/README.md
+++ b/huff_core/README.md
@@ -19,9 +19,11 @@ use huff_core::Compiler;
 use huff_utils::error::CompilerError;
 use huff_utils::artifact::Artifact;
 use std::sync::Arc;
+use std::cell::RefCell;
+use std::rc::Rc;
 
 // Instantiate the Compiler Instance
-let mut compiler = Compiler::new(Arc::new(vec!["../huff-examples/erc20/contracts/ERC20.huff".to_string()]), None, None, false, false);
+let mut compiler = Compiler::new(Arc::new(vec!["../huff-examples/erc20/contracts/ERC20.huff".to_string()]), None, None, None, false, false);
 
 // Execute the compiler
 let res: Result<Vec<Arc<Artifact>>, Arc<CompilerError<'_>>> = compiler.execute();
diff --git a/huff_core/src/cache.rs b/huff_core/src/cache.rs
index 3a5fef0..004fe5c 100644
--- a/huff_core/src/cache.rs
+++ b/huff_core/src/cache.rs
@@ -31,7 +31,7 @@ pub fn resolve_existing_artifacts(
         files.iter().map(|f| (f.path.clone().to_lowercase(), Arc::clone(f))).collect();
 
     // If outputdir is not specified, use the default "./artifacts/" directory
-    let output_dir = (!output.0.is_empty()).then(|| &*output.0).unwrap_or("./artifacts");
+    let output_dir = (!output.0.is_empty()).then_some(&*output.0).unwrap_or("./artifacts");
 
     // For each file, check if the artifact file exists at the location
     tracing::debug!(target: "core", "Traversing output directory {}", output_dir);
diff --git a/huff_core/src/lib.rs b/huff_core/src/lib.rs
index 90a80f7..4e595ae 100644
--- a/huff_core/src/lib.rs
+++ b/huff_core/src/lib.rs
@@ -11,6 +11,7 @@ use huff_parser::*;
 use huff_utils::prelude::*;
 use rayon::prelude::*;
 use std::{
+    collections::BTreeMap,
     ffi::OsString,
     fs,
     path::{Path, PathBuf},
@@ -42,18 +43,21 @@ pub(crate) mod cache;
 ///     Arc::new(vec!["../huff-examples/erc20/contracts/ERC20.huff".to_string()]),
 ///     Some("./artifacts".to_string()),
 ///     None,
+///     None,
 ///     false,
 ///     false
 /// );
 /// ```
 #[derive(Default, Debug, Clone)]
-pub struct Compiler {
+pub struct Compiler<'a> {
     /// The location of the files to compile
     pub sources: Arc<Vec<String>>,
     /// The output location
     pub output: Option<String>,
     /// Constructor Input Arguments
     pub construct_args: Option<Vec<String>>,
+    /// Constant Overrides
+    pub constant_overrides: Option<BTreeMap<&'a str, Literal>>,
     /// Whether to optimize compilation or not.
     pub optimize: bool,
     /// Generate and log bytecode
@@ -62,19 +66,28 @@ pub struct Compiler {
     pub cached: bool,
 }
 
-impl<'a> Compiler {
+impl<'a> Compiler<'a> {
     /// Public associated function to instantiate a new compiler.
     pub fn new(
         sources: Arc<Vec<String>>,
         output: Option<String>,
         construct_args: Option<Vec<String>>,
+        constant_overrides: Option<BTreeMap<&'a str, Literal>>,
         verbose: bool,
         cached: bool,
     ) -> Self {
         if cfg!(feature = "verbose") || verbose {
             Compiler::init_tracing_subscriber(Some(vec![tracing::Level::INFO.into()]));
         }
-        Self { sources, output, construct_args, optimize: false, bytecode: false, cached }
+        Self {
+            sources,
+            output,
+            construct_args,
+            constant_overrides,
+            optimize: false,
+            bytecode: false,
+            cached,
+        }
     }
 
     /// Tracing
@@ -230,6 +243,7 @@ impl<'a> Compiler {
         let parse_res = parser.parse().map_err(CompilerError::ParserError);
         let mut contract = parse_res?;
         contract.derive_storage_pointers();
+        contract.add_override_constants(&self.constant_overrides);
         tracing::info!(target: "core", "PARSED CONTRACT [{}]", file.path);
 
         // Primary Bytecode Generation
@@ -238,6 +252,7 @@ impl<'a> Compiler {
         let main_bytecode = match Codegen::generate_main_bytecode(&contract) {
             Ok(mb) => mb,
             Err(mut e) => {
+                tracing::error!(target: "codegen", "FAILED TO GENERATE MAIN BYTECODE FOR CONTRACT");
                 // Add File Source to Span
                 e.span = AstSpan(
                     e.span
@@ -258,21 +273,28 @@ impl<'a> Compiler {
         let constructor_bytecode = match Codegen::generate_constructor_bytecode(&contract) {
             Ok(mb) => mb,
             Err(mut e) => {
-                if !inputs.is_empty() {
+                // Return any errors except if the inputs is empty and the constructor definition is
+                // missing
+                if e.kind != CodegenErrorKind::MissingMacroDefinition("CONSTRUCTOR".to_string()) ||
+                    !inputs.is_empty()
+                {
                     // Add File Source to Span
-                    e.span = AstSpan(
-                        e.span
-                            .0
-                            .into_iter()
-                            .map(|mut s| {
-                                s.file = Some(Arc::clone(&file));
-                                s
-                            })
-                            .collect::<Vec<Span>>(),
-                    );
+                    let mut errs = e
+                        .span
+                        .0
+                        .into_iter()
+                        .map(|mut s| {
+                            s.file = Some(Arc::clone(&file));
+                            s
+                        })
+                        .collect::<Vec<Span>>();
+                    errs.dedup();
+                    e.span = AstSpan(errs);
                     tracing::error!(target: "codegen", "Constructor inputs provided, but contract missing \"CONSTRUCTOR\" macro!");
                     return Err(CompilerError::CodegenError(e))
                 }
+
+                // If the kind is a missing constructor we can ignore it
                 tracing::warn!(target: "codegen", "Contract has no \"CONSTRUCTOR\" macro definition!");
                 "".to_string()
             }
diff --git a/huff_lexer/src/lib.rs b/huff_lexer/src/lib.rs
index 3c9216b..1640a27 100644
--- a/huff_lexer/src/lib.rs
+++ b/huff_lexer/src/lib.rs
@@ -32,6 +32,8 @@ pub enum Context {
     AbiArgs,
     /// constant context
     Constant,
+    /// Code table context
+    CodeTableBody,
 }
 
 /// ## Lexer
@@ -266,7 +268,7 @@ impl<'a> Lexer<'a> {
     /// `TokenKind::Ident`.
     ///
     /// Rules:
-    /// - The `macro`, `function`, `constant`, `event`, `jumptable`, `jumptable__packed`, and
+    /// - The `macro`, `fn`, `function`, `constant`, `event`, `jumptable`, `jumptable__packed`, and
     ///   `table` keywords must be preceded by a `#define` keyword.
     /// - The `takes` keyword must be preceded by an assignment operator: `=`.
     /// - The `nonpayable`, `payable`, `view`, and `pure` keywords must be preceeded by one of these
@@ -276,6 +278,7 @@ impl<'a> Lexer<'a> {
     pub fn check_keyword_rules(&mut self, found_kind: &Option<TokenKind>) -> bool {
         match found_kind {
             Some(TokenKind::Macro) |
+            Some(TokenKind::Fn) |
             Some(TokenKind::Function) |
             Some(TokenKind::Constant) |
             Some(TokenKind::Event) |
@@ -377,6 +380,7 @@ impl<'a> Iterator for Lexer<'a> {
 
                     let keys = [
                         TokenKind::Macro,
+                        TokenKind::Fn,
                         TokenKind::Function,
                         TokenKind::Constant,
                         TokenKind::Takes,
@@ -417,9 +421,12 @@ impl<'a> Iterator for Lexer<'a> {
 
                     if let Some(kind) = &found_kind {
                         match kind {
-                            TokenKind::Macro => self.context = Context::MacroDefinition,
+                            TokenKind::Macro | TokenKind::Fn => {
+                                self.context = Context::MacroDefinition
+                            }
                             TokenKind::Function | TokenKind::Event => self.context = Context::Abi,
                             TokenKind::Constant => self.context = Context::Constant,
+                            TokenKind::CodeTable => self.context = Context::CodeTableBody,
                             _ => (),
                         }
                     }
@@ -454,7 +461,7 @@ impl<'a> Iterator for Lexer<'a> {
                         }
                     }
 
-                    let pot_op = self.dyn_peek(|c| c.is_alphanumeric());
+                    let pot_op = self.dyn_peek(|c| c.is_alphanumeric() || c == &'_');
 
                     // Syntax sugar: true evaluates to 0x01, false evaluates to 0x00
                     if matches!(pot_op.as_str(), "true" | "false") {
@@ -575,7 +582,15 @@ impl<'a> Iterator for Lexer<'a> {
                             matches!(c, '\u{0041}'..='\u{0046}' | '\u{0061}'..='\u{0066}')
                     });
                     self.current_span_mut().start += 2; // Ignore the "0x"
-                    TokenKind::Literal(str_to_bytes32(self.slice().as_ref()))
+
+                    if self.context == Context::CodeTableBody {
+                        // In codetables, the bytecode provided is of arbitrary length. We pass
+                        // the code as an Ident, and it is appended to the end of the runtime
+                        // bytecode in codegen.
+                        TokenKind::Ident(self.slice())
+                    } else {
+                        TokenKind::Literal(str_to_bytes32(self.slice().as_ref()))
+                    }
                 }
                 '=' => TokenKind::Assign,
                 '(' => {
@@ -603,7 +618,7 @@ impl<'a> Iterator for Lexer<'a> {
                     TokenKind::OpenBrace
                 }
                 '}' => {
-                    if self.context == Context::MacroBody {
+                    if matches!(self.context, Context::MacroBody | Context::CodeTableBody) {
                         self.context = Context::Global;
                     }
                     TokenKind::CloseBrace
diff --git a/huff_parser/Cargo.toml b/huff_parser/Cargo.toml
index 031d06b..8706b56 100644
--- a/huff_parser/Cargo.toml
+++ b/huff_parser/Cargo.toml
@@ -18,3 +18,4 @@ proptest = "1.0.0"
 tiny-keccak = { version = "2.0.2", features = ["keccak"] }
 tracing = "0.1.34"
 hex = "0.4.3"
+regex = "1.6.0"
\ No newline at end of file
diff --git a/huff_parser/README.md b/huff_parser/README.md
index 904c072..dc42b52 100644
--- a/huff_parser/README.md
+++ b/huff_parser/README.md
@@ -19,6 +19,8 @@ definition.
 use huff_utils::prelude::*;
 use huff_lexer::{Lexer};
 use huff_parser::{Parser};
+use std::cell::RefCell;
+use std::rc::Rc;
 
 // Create a Lexer from the source code
 let source = "#define macro HELLO_WORLD() = takes(0) returns(0) {}";
@@ -45,11 +47,12 @@ let expected_contract = Contract {
       takes: 0,
       returns: 0,
       span: AstSpan(vec![Span { start: 0, end: 7, file: None }, Span { start: 8, end: 13, file: None }, Span { start: 14, end: 25, file: None }, Span { start: 25, end: 26, file: None }, Span { start: 26, end: 27, file: None }, Span { start: 28, end: 29, file: None }, Span { start: 30, end: 35, file: None }, Span { start: 35, end: 36, file: None }, Span { start: 36, end: 37, file: None }, Span { start: 37, end: 38, file: None }, Span { start: 39, end: 46, file: None }, Span { start: 46, end: 47, file: None }, Span { start: 47, end: 48, file: None }, Span { start: 48, end: 49, file: None }, Span { start: 50, end: 51, file: None }, Span { start: 51, end: 52, file: None }]),
+      outlined: false,
     }
   ],
   invocations: vec![],
   imports: vec![],
-  constants: vec![],
+  constants: Rc::new(RefCell::new(vec![])),
   functions: vec![],
   events: vec![],
   tables: vec![],
diff --git a/huff_parser/src/lib.rs b/huff_parser/src/lib.rs
index 96e4325..7dcd1a0 100644
--- a/huff_parser/src/lib.rs
+++ b/huff_parser/src/lib.rs
@@ -11,6 +11,7 @@ use huff_utils::{
     token::{Token, TokenKind},
     types::*,
 };
+use regex::Regex;
 use std::path::Path;
 use tiny_keccak::{Hasher, Keccak};
 
@@ -85,9 +86,9 @@ impl Parser {
                 TokenKind::Constant => {
                     let c = self.parse_constant()?;
                     tracing::info!(target: "parser", "SUCCESSFULLY PARSED CONSTANT {}", c.name);
-                    contract.constants.push(c);
+                    contract.constants.borrow_mut().push(c);
                 }
-                TokenKind::Macro => {
+                TokenKind::Macro | TokenKind::Fn => {
                     let m = self.parse_macro()?;
                     tracing::info!(target: "parser", "SUCCESSFULLY PARSED MACRO {}", m.name);
                     contract.macros.push(m);
@@ -103,7 +104,7 @@ impl Parser {
                     );
                     return Err(ParserError {
                         kind: ParserErrorKind::InvalidDefinition(self.current_token.kind.clone()),
-                        hint: Some("Definition must be one of: `function`, `event`, `constant`, or `macro`.".to_string()),
+                        hint: Some("Definition must be one of: `function`, `event`, `constant`, `macro`, or `fn`.".to_string()),
                         spans: AstSpan(vec![self.current_token.span.clone()]),
                     })
                 }
@@ -377,7 +378,8 @@ impl Parser {
     ///
     /// It should parse the following : macro MACRO_NAME(args...) = takes (x) returns (n) {...}
     pub fn parse_macro(&mut self) -> Result<MacroDefinition, ParserError> {
-        self.match_kind(TokenKind::Macro)?;
+        let outlined = self.check(TokenKind::Fn);
+        self.match_kind(if outlined { TokenKind::Fn } else { TokenKind::Macro })?;
         let macro_name: String =
             self.match_kind(TokenKind::Ident("MACRO_NAME".to_string()))?.to_string();
         tracing::info!(target: "parser", "PARSING MACRO: \"{}\"", macro_name);
@@ -397,6 +399,7 @@ impl Parser {
             macro_takes,
             macro_returns,
             self.spans.clone(),
+            outlined,
         ))
     }
 
@@ -776,6 +779,7 @@ impl Parser {
     ///
     /// It should parse the following : (jumptable|jumptable__packed|table) NAME() {...}
     pub fn parse_table(&mut self) -> Result<TableDefinition, ParserError> {
+        let is_code_table = self.current_token.kind == TokenKind::CodeTable;
         let kind = TableKind::from(self.match_kind(self.current_token.kind.clone())?);
         let table_name: String =
             self.match_kind(TokenKind::Ident("TABLE_NAME".to_string()))?.to_string();
@@ -786,7 +790,7 @@ impl Parser {
         let _ = self.match_kind(TokenKind::Assign);
 
         // Parse the core table
-        let table_statements: Vec<Statement> = self.parse_table_body()?;
+        let table_statements: Vec<Statement> = self.parse_table_body(is_code_table)?;
         let size = match kind {
             TableKind::JumpTablePacked => table_statements.len() * 0x02,
             TableKind::JumpTable => table_statements.len() * 0x20,
@@ -794,13 +798,13 @@ impl Parser {
                 table_statements
                     .iter()
                     .map(|s| {
-                        if let StatementType::LabelCall(l) = &s.ty {
-                            l.len()
+                        if let StatementType::Code(c) = &s.ty {
+                            c.len()
                         } else {
                             // TODO: Throw an error here.
                             tracing::error!(
                                 target: "parser",
-                                "Invalid table statement. Must be a label call. Got: {:?}",
+                                "Invalid table statement. Must be valid hex bytecode. Got: {:?}",
                                 s
                             );
                             0_usize
@@ -815,30 +819,49 @@ impl Parser {
             table_name,
             kind,
             table_statements,
-            str_to_bytes32(size.to_string().as_str()),
+            str_to_bytes32(format!("{:02x}", size).as_str()),
             AstSpan(self.spans.clone()),
         ))
     }
 
     /// Parse the body of a table.
     ///
-    /// Only `LabelCall`s should be authorized.
-    /// TODO: Code tables are not yet supported.
-    pub fn parse_table_body(&mut self) -> Result<Vec<Statement>, ParserError> {
+    /// Only `LabelCall` and `Code` Statements should be authorized.
+    pub fn parse_table_body(&mut self, is_code_table: bool) -> Result<Vec<Statement>, ParserError> {
         let mut statements: Vec<Statement> = Vec::new();
+        let code_statement_regex = Regex::new(r"^([a-fA-F\d]+)$").unwrap();
+
         self.match_kind(TokenKind::OpenBrace)?;
         while !self.check(TokenKind::CloseBrace) {
             let new_spans = vec![self.current_token.span.clone()];
             match &self.current_token.kind {
                 TokenKind::Ident(ident_str) => {
                     statements.push(Statement {
-                        ty: StatementType::LabelCall(ident_str.to_string()),
+                        ty: if is_code_table {
+                            if code_statement_regex.is_match(ident_str) {
+                                StatementType::Code(ident_str.to_string())
+                            } else {
+                                tracing::error!(
+                                    "Invalid CodeTable Body Token: {:?}",
+                                    self.current_token.kind
+                                );
+                                return Err(ParserError {
+                                    kind: ParserErrorKind::InvalidTableBodyToken(
+                                        self.current_token.kind.clone(),
+                                    ),
+                                    hint: Some("Expected valid hex bytecode.".to_string()),
+                                    spans: AstSpan(new_spans),
+                                })
+                            }
+                        } else {
+                            StatementType::LabelCall(ident_str.to_string())
+                        },
                         span: AstSpan(new_spans),
                     });
                     self.consume();
                 }
                 kind => {
-                    tracing::error!("Invalid Table Body Token: {:?}", self.current_token.kind);
+                    tracing::error!("Invalid Table Body Token: {:?}", kind);
                     return Err(ParserError {
                         kind: ParserErrorKind::InvalidTableBodyToken(kind.clone()),
                         hint: Some("Expected an identifier string.".to_string()),
diff --git a/huff_utils/src/abi.rs b/huff_utils/src/abi.rs
index e38d76b..72a88c0 100644
--- a/huff_utils/src/abi.rs
+++ b/huff_utils/src/abi.rs
@@ -9,6 +9,8 @@
 //! instance like so:
 //!
 //! ```rust
+//! use std::cell::RefCell;
+//! use std::rc::Rc;
 //! use huff_utils::prelude::*;
 //!
 //! // Generate a default contract for demonstrative purposes.
@@ -17,7 +19,7 @@
 //!     macros: vec![],
 //!     invocations: vec![],
 //!     imports: vec![],
-//!     constants: vec![],
+//!     constants: Rc::new(RefCell::new(vec![])),
 //!     functions: vec![huff_utils::ast::Function {
 //!         name: "CONSTRUCTOR".to_string(),
 //!         signature: [0u8, 0u8, 0u8, 0u8],
diff --git a/huff_utils/src/ast.rs b/huff_utils/src/ast.rs
index 5a010ee..b1d849e 100644
--- a/huff_utils/src/ast.rs
+++ b/huff_utils/src/ast.rs
@@ -9,9 +9,11 @@ use crate::{
     prelude::{Span, TokenKind},
 };
 use std::{
+    cell::RefCell,
     collections::BTreeMap,
     fmt::{Display, Formatter},
     path::PathBuf,
+    rc::Rc,
 };
 
 /// A contained literal
@@ -96,7 +98,7 @@ pub struct Contract {
     /// File Imports
     pub imports: Vec<FilePath>,
     /// Constants
-    pub constants: Vec<ConstantDefinition>,
+    pub constants: Rc<RefCell<Vec<ConstantDefinition>>>,
     /// Functions
     pub functions: Vec<Function>,
     /// Events
@@ -159,7 +161,7 @@ impl Contract {
         tracing::debug!(target: "ast", "ALL AST CONSTANTS: {:?}", storage_pointers);
 
         // Set all the constants to their new values
-        for c in &mut self.constants {
+        for c in self.constants.borrow_mut().iter_mut() {
             match storage_pointers
                 .iter()
                 .filter(|pointer| pointer.0.eq(&c.name))
@@ -217,6 +219,7 @@ impl Contract {
                         // Get the associated constant
                         match self
                             .constants
+                            .borrow()
                             .iter()
                             .filter(|c| c.name.eq(const_name))
                             .collect::<Vec<&ConstantDefinition>>()
@@ -291,6 +294,29 @@ impl Contract {
         //     self.recurse_ast_constants(next_md, storage_pointers, last_p, macros_to_recurse);
         // }
     }
+
+    /// Add override constants to the AST
+    ///
+    /// ## Overview
+    ///
+    /// For each override constant, add it to the AST if it doesn't already exist. Override
+    /// constants can be passed in via the CLI.
+    pub fn add_override_constants(&self, override_constants: &Option<BTreeMap<&str, Literal>>) {
+        if let Some(override_constants) = override_constants {
+            for (name, value) in override_constants {
+                let mut constants = self.constants.borrow_mut();
+                if let Some(c) = constants.iter_mut().find(|c| c.name.as_str().eq(*name)) {
+                    c.value = ConstVal::Literal(*value);
+                } else {
+                    constants.push(ConstantDefinition {
+                        name: name.to_string(),
+                        value: ConstVal::Literal(*value),
+                        span: AstSpan::default(),
+                    });
+                }
+            }
+        }
+    }
 }
 
 /// A function, event, or macro argument
@@ -427,6 +453,8 @@ pub struct MacroDefinition {
     pub returns: usize,
     /// The Span of the Macro Definition
     pub span: AstSpan,
+    /// Is the macro a function (outlined)?
+    pub outlined: bool,
 }
 
 impl ToIRBytecode<CodegenError> for MacroDefinition {
@@ -445,8 +473,17 @@ impl MacroDefinition {
         takes: usize,
         returns: usize,
         spans: Vec<Span>,
+        outlined: bool,
     ) -> Self {
-        MacroDefinition { name, parameters, statements, takes, returns, span: AstSpan(spans) }
+        MacroDefinition {
+            name,
+            parameters,
+            statements,
+            takes,
+            returns,
+            span: AstSpan(spans),
+            outlined,
+        }
     }
 
     /// Translate statements into IRBytes
@@ -470,6 +507,12 @@ impl MacroDefinition {
                         span: statement.span.clone(),
                     });
                 }
+                StatementType::Code(c) => {
+                    inner_irbytes.push(IRBytes {
+                        ty: IRByteType::Bytes(Bytes(c.to_owned())),
+                        span: statement.span.clone(),
+                    });
+                }
                 StatementType::MacroInvocation(mi) => {
                     inner_irbytes.push(IRBytes {
                         ty: IRByteType::Statement(Statement {
@@ -482,19 +525,19 @@ impl MacroDefinition {
                 StatementType::Constant(name) => {
                     // Constant needs to be evaluated at the top-level
                     inner_irbytes.push(IRBytes {
-                        ty: IRByteType::Constant(name.to_string()),
+                        ty: IRByteType::Constant(name.to_owned()),
                         span: statement.span.clone(),
                     });
                 }
                 StatementType::ArgCall(arg_name) => {
                     // Arg call needs to use a destination defined in the calling macro context
                     inner_irbytes.push(IRBytes {
-                        ty: IRByteType::ArgCall(arg_name.to_string()),
+                        ty: IRByteType::ArgCall(arg_name.to_owned()),
                         span: statement.span.clone(),
                     });
                 }
                 StatementType::LabelCall(jump_to) => {
-                    /* Jump To doesn't translate directly to bytecode ? */
+                    /* Jump To doesn't translate directly to bytecode */
                     inner_irbytes.push(IRBytes {
                         ty: IRByteType::Statement(Statement {
                             ty: StatementType::LabelCall(jump_to.to_string()),
@@ -504,7 +547,7 @@ impl MacroDefinition {
                     });
                 }
                 StatementType::Label(l) => {
-                    /* Jump Dests don't translate directly to bytecode ? */
+                    /* Jump Dests don't translate directly to bytecode */
                     inner_irbytes.push(IRBytes {
                         ty: IRByteType::Statement(Statement {
                             ty: StatementType::Label(l.clone()),
@@ -646,6 +689,8 @@ pub enum StatementType {
     Literal(Literal),
     /// An Opcode Statement
     Opcode(Opcode),
+    /// A Code Statement
+    Code(String),
     /// A Macro Invocation Statement
     MacroInvocation(MacroInvocation),
     /// A Constant Push
@@ -665,6 +710,7 @@ impl Display for StatementType {
         match self {
             StatementType::Literal(l) => write!(f, "LITERAL: {}", bytes32_to_string(l, true)),
             StatementType::Opcode(o) => write!(f, "OPCODE: {}", o),
+            StatementType::Code(s) => write!(f, "CODE: {}", s),
             StatementType::MacroInvocation(m) => {
                 write!(f, "MACRO INVOCATION: {}", m.macro_name)
             }
diff --git a/huff_utils/src/bytecode.rs b/huff_utils/src/bytecode.rs
index 1f8e82b..9612c93 100644
--- a/huff_utils/src/bytecode.rs
+++ b/huff_utils/src/bytecode.rs
@@ -2,7 +2,7 @@
 //!
 //! Abstract translating state into bytecode.
 
-use crate::prelude::{AstSpan, Statement};
+use crate::prelude::{AstSpan, Statement, TableDefinition};
 use std::{
     collections::BTreeMap,
     fmt::{self, Display},
@@ -76,6 +76,8 @@ pub struct BytecodeRes {
     pub unmatched_jumps: Jumps,
     /// Table Instances
     pub table_instances: Jumps,
+    /// Utilized Tables
+    pub utilized_tables: Vec<TableDefinition>,
 }
 
 impl Display for BytecodeRes {
diff --git a/huff_utils/src/error.rs b/huff_utils/src/error.rs
index ded95ef..f24de3c 100644
--- a/huff_utils/src/error.rs
+++ b/huff_utils/src/error.rs
@@ -157,6 +157,10 @@ pub enum CodegenErrorKind {
     UsizeConversion(String),
     /// Invalid Arguments
     InvalidArguments(String),
+    /// Invalid Table Statement
+    InvalidTableStatement(String),
+    /// Invalid Code Length
+    InvalidCodeLength(usize),
 }
 
 impl Spanned for CodegenError {
@@ -200,6 +204,12 @@ impl<W: Write> Report<W> for CodegenError {
             CodegenErrorKind::InvalidArguments(msg) => {
                 write!(f.out, "Invalid arguments: \"{}\"", msg)
             }
+            CodegenErrorKind::InvalidTableStatement(msg) => {
+                write!(f.out, "Invalid table statement: \"{}\"", msg)
+            }
+            CodegenErrorKind::InvalidCodeLength(len) => {
+                write!(f.out, "Invalid code length: {}", len)
+            }
         }
     }
 }
@@ -489,6 +499,12 @@ impl<'a> fmt::Display for CompilerError<'a> {
                 CodegenErrorKind::InvalidArguments(_) => {
                     write!(f, "\nError: Invalid Arguments\n{}\n", ce.span.error(None))
                 }
+                CodegenErrorKind::InvalidTableStatement(_) => {
+                    write!(f, "\nError: Invalid Table Statement\n{}\n", ce.span.error(None))
+                }
+                CodegenErrorKind::InvalidCodeLength(_) => {
+                    write!(f, "\nError: Invalid Code Length\n{}\n", ce.span.error(None))
+                }
             },
             CompilerError::FailedCompiles(v) => {
                 v.iter().for_each(|ce| {
diff --git a/huff_utils/src/files.rs b/huff_utils/src/files.rs
index ee7a09b..308dbc7 100644
--- a/huff_utils/src/files.rs
+++ b/huff_utils/src/files.rs
@@ -170,7 +170,7 @@ impl Span {
 
     /// Converts a span to a range.
     pub fn range(&self) -> Option<Range<usize>> {
-        (*self != Self::EOF).then(|| self.start..self.end)
+        (*self != Self::EOF).then_some(self.start..self.end)
     }
 
     /// Produces a file identifier string for errors
diff --git a/huff_utils/src/token.rs b/huff_utils/src/token.rs
index 67a8a71..c572a7b 100644
--- a/huff_utils/src/token.rs
+++ b/huff_utils/src/token.rs
@@ -35,6 +35,8 @@ pub enum TokenKind {
     Include,
     /// "macro" keyword
     Macro,
+    /// "fn" keyword
+    Fn,
     /// "function" keyword
     Function,
     /// "event" keyword
@@ -126,6 +128,7 @@ impl fmt::Display for TokenKind {
             TokenKind::Define => "#define",
             TokenKind::Include => "#include",
             TokenKind::Macro => "macro",
+            TokenKind::Fn => "fn",
             TokenKind::Function => "function",
             TokenKind::Event => "event",
             TokenKind::Constant => "constant",


Test patch:
diff --git a/huff_codegen/tests/abigen.rs b/huff_codegen/tests/abigen.rs
index 7473956..4c30347 100644
--- a/huff_codegen/tests/abigen.rs
+++ b/huff_codegen/tests/abigen.rs
@@ -1,4 +1,4 @@
-use std::collections::BTreeMap;
+use std::{cell::RefCell, collections::BTreeMap, rc::Rc};
 
 use huff_codegen::Codegen;
 use huff_utils::{ast, prelude::*};
@@ -12,12 +12,13 @@ fn constructs_valid_abi() {
         takes: 0,
         returns: 0,
         span: AstSpan(vec![]),
+        outlined: false,
     };
     let contract = Contract {
         macros: vec![constructor],
         invocations: vec![],
         imports: vec![],
-        constants: vec![],
+        constants: Rc::new(RefCell::new(vec![])),
         functions: vec![],
         events: vec![],
         tables: vec![],
@@ -40,19 +41,20 @@ fn constructs_valid_abi() {
 
 #[test]
 fn missing_constructor_fails() {
-    let _constructor = ast::MacroDefinition {
+    let _constructor = MacroDefinition {
         name: "CONSTRUCTOR".to_string(),
         parameters: vec![],
         statements: vec![],
         takes: 0,
         returns: 0,
         span: AstSpan(vec![]),
+        outlined: false,
     };
     let contract = Contract {
         macros: vec![],
         invocations: vec![],
         imports: vec![],
-        constants: vec![],
+        constants: Rc::new(RefCell::new(vec![])),
         functions: vec![],
         events: vec![],
         tables: vec![],
diff --git a/huff_core/tests/builtins.rs b/huff_core/tests/builtins.rs
index 8851dd7..9c5914c 100644
--- a/huff_core/tests/builtins.rs
+++ b/huff_core/tests/builtins.rs
@@ -58,6 +58,10 @@ fn test_tablesize_builtin() {
             lab_3 // 3 00011
         }
 
+        #define table CODE_TABLE {
+            0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF
+        }
+
         #define macro TEST_INIT_JUMP_TABLE() = takes(0) returns(1) {
             __tablesize(STANDARD_JUMPTABLE) __tablestart(STANDARD_JUMPTABLE) 0x00 codecopy
         }
@@ -66,8 +70,14 @@ fn test_tablesize_builtin() {
             __tablesize(PACKED_JUMPTABLE)
         }
 
+        #define macro BUILTIN_TEST_2() = takes(0) returns(1) {
+            __tablesize(CODE_TABLE)
+        }
+
         #define macro MAIN() = takes(0) returns (0) {
             BUILTIN_TEST()
+            TEST_INIT_JUMP_TABLE()
+            BUILTIN_TEST_2()
 
             lab_0:
                 0x00
@@ -108,7 +118,7 @@ fn test_tablesize_builtin() {
 
     // Have the Codegen create the constructor bytecode
     let mbytes = Codegen::generate_main_bytecode(&contract).unwrap();
-    assert_eq!(mbytes, String::from("60085b60006000f35b60006000f35b60006000f35b60006000f300020008000e001400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000014"));
+    assert_eq!(mbytes, String::from("6008608061002c60003960205b60006000f35b60006000f35b60006000f35b60006000f3000c00120018001e000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001eDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"));
 }
 
 #[test]
@@ -125,7 +135,7 @@ fn test_tablestart_builtin() {
             lab_3 // 3 00011
         }
 
-        #define macro BUILTIN_TEST() = takes(0) returns(1) {
+        #define macro BUILTIN_TEST() = takes(0) returns(2) {
             __tablestart(PACKED_JUMPTABLE)
             __tablestart(STANDARD_JUMPTABLE)
         }
@@ -235,7 +245,7 @@ fn test_jump_table_exhaustive_usage() {
 
     // Have the Codegen create the constructor bytecode
     let mbytes = Codegen::generate_main_bytecode(&contract).unwrap();
-    assert_eq!(mbytes, String::from("61012861004160003960003560e01c8063a9059cbb1461001a575b60208703516202ffe016806020015b60206020015b60206020015b60206020015b60206020010000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003b"));
+    assert_eq!(mbytes, String::from("608061004060003960003560e01c8063a9059cbb14610019575b60208703516202ffe016806020015b60206020015b60206020015b60206020015b60206020010000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000003a"));
 }
 
 #[test]
@@ -362,7 +372,7 @@ fn test_label_clashing() {
 
     // Have the Codegen create the main macro bytecode
     let mbytes = Codegen::generate_main_bytecode(&contract).unwrap();
-    assert_eq!(mbytes, String::from("600861004960003961012861005160003960003560e01c8063a9059cbb14610022575b60208703516202ffe016806020015b60206020015b60206020015b60206020015b602060200100310037003d004300000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000037000000000000000000000000000000000000000000000000000000000000003d0000000000000000000000000000000000000000000000000000000000000043"));
+    assert_eq!(mbytes, String::from("6008610048600039608061005060003960003560e01c8063a9059cbb14610021575b60208703516202ffe016806020015b60206020015b60206020015b60206020015b602060200100300036003c004200000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000042"));
 }
 
 #[test]
diff --git a/huff_core/tests/file_resolution.rs b/huff_core/tests/file_resolution.rs
index 507ed36..7b215e0 100644
--- a/huff_core/tests/file_resolution.rs
+++ b/huff_core/tests/file_resolution.rs
@@ -5,7 +5,7 @@ use huff_utils::prelude::{CompilerError, OutputLocation, UnpackError};
 
 #[test]
 fn test_get_outputs_no_output() {
-    let compiler: Compiler = Compiler::new(Arc::new(vec![]), None, None, false, false);
+    let compiler: Compiler = Compiler::new(Arc::new(vec![]), None, None, None, false, false);
     let ol: OutputLocation = compiler.get_outputs();
     assert_eq!(ol, OutputLocation::default());
 }
@@ -13,7 +13,7 @@ fn test_get_outputs_no_output() {
 #[test]
 fn test_get_outputs_with_output() {
     let compiler: Compiler =
-        Compiler::new(Arc::new(vec![]), Some("./test_out/".to_string()), None, false, false);
+        Compiler::new(Arc::new(vec![]), Some("./test_out/".to_string()), None, None, false, false);
     let ol: OutputLocation = compiler.get_outputs();
     assert_eq!(ol, OutputLocation("./test_out/".to_string()));
 }
@@ -21,7 +21,7 @@ fn test_get_outputs_with_output() {
 #[test]
 fn test_transform_paths() {
     let _compiler: Compiler =
-        Compiler::new(Arc::new(vec![]), Some("./test_out/".to_string()), None, false, false);
+        Compiler::new(Arc::new(vec![]), Some("./test_out/".to_string()), None, None, false, false);
     let path_bufs: Result<Vec<PathBuf>, CompilerError<'_>> = Compiler::transform_paths(&vec![
         "../huff-examples/erc20/contracts/ERC20.huff".to_string(),
         "../huff-examples/erc20/contracts/utils/".to_string(),
@@ -55,7 +55,7 @@ fn test_transform_paths() {
 #[test]
 fn test_transform_paths_non_huff() {
     let _compiler: Compiler =
-        Compiler::new(Arc::new(vec![]), Some("./test_out/".to_string()), None, false, false);
+        Compiler::new(Arc::new(vec![]), Some("./test_out/".to_string()), None, None, false, false);
     let path_bufs: Result<Vec<PathBuf>, CompilerError<'_>> =
         Compiler::transform_paths(&vec!["./ERC20.txt".to_string()]);
     assert!(path_bufs.is_err());
@@ -72,7 +72,7 @@ fn test_transform_paths_non_huff() {
 #[test]
 fn test_transform_paths_no_dir() {
     let _compiler: Compiler =
-        Compiler::new(Arc::new(vec![]), Some("./test_out/".to_string()), None, false, false);
+        Compiler::new(Arc::new(vec![]), Some("./test_out/".to_string()), None, None, false, false);
     let path_bufs: Result<Vec<PathBuf>, CompilerError<'_>> =
         Compiler::transform_paths(&vec!["./examples/random_dir/".to_string()]);
     assert!(path_bufs.is_err());
diff --git a/huff_core/tests/functions.rs b/huff_core/tests/functions.rs
new file mode 100644
index 0000000..555e4db
--- /dev/null
+++ b/huff_core/tests/functions.rs
@@ -0,0 +1,213 @@
+use huff_codegen::Codegen;
+use huff_lexer::Lexer;
+use huff_parser::Parser;
+use huff_utils::prelude::{FileSource, FullFileSource, Token};
+use std::sync::Arc;
+
+#[test]
+fn test_function() {
+    let source: &str = r#"
+        #define function test1(uint256,uint256,uint256) pure returns(uint256)
+        #define function test2(uint256,uint256,uint256) pure returns(uint256)
+        #define function test3(uint256,uint256,uint256) pure returns(uint256)
+
+        #define fn MUL_DIV_DOWN() = takes (3) returns (1) {
+            // Input stack:      [x, y, denominator]
+            dup3              // [denominator, x, y, denominator]
+            dup3              // [y, denominator, x, y, denominator]
+            dup3              // [x, y, denominator, x, y, denominator]
+
+            mul               // [x * y, denominator, x, y, denominator]
+            0x00 mstore       // [denominator, x, y, denominator]
+
+            iszero iszero     // [denominator != 0, x, y, denominator]
+
+            swap1             // [x, denominator != 0, y, denominator]
+            dup1              // [x, x, denominator != 0, y, denominator]
+            iszero            // [x == 0, x, denominator != 0, y, denominator]
+
+            swap1             // [x, x == 0, denominator != 0, y, denominator]
+            0x00 mload        // [x * y, x, x == 0, denominator != 0, y, denominator]
+            div               // [(x * y) / x, x == 0, denominator != 0, y, denominator]
+
+            dup4              // [y, (x * y) / x, x == 0, denominator != 0, y, denominator]
+            eq                // [y == (x * y) / x, x == 0, denominator != 0, y, denominator]
+            or                // [y == (x * y) / x | x == 0, denominator != 0, y, denominator]
+            and               // [(y == (x * y) / x | x == 0) & denominator != 0, y, denominator]
+
+            iszero fail jumpi // Revert if (y == (x * y) / x | x == 0) & denominator != 0 is not satisfied
+
+            pop               // [denominator]
+            0x00 mload        // [x * y, denominator]
+            div               // [(x * y) / denominator]
+
+            0x01 finish jumpi
+
+            fail:
+                0x00 0x00 revert
+            finish:
+            // Return stack:     [(x * y) / denominator]
+        }
+
+        #define macro TEST_1() = takes(0) returns(0) {
+            0x44 calldataload // [denominator]
+            0x24 calldataload // [y, denominator]
+            0x04 calldataload // [x, y, denominator]
+            MUL_DIV_DOWN()
+            0x00 mstore
+            0x20 0x00 return
+        }
+
+        #define macro TEST_2() = takes(0) returns(0) {
+            0x44 calldataload // [denominator]
+            0x24 calldataload // [y, denominator]
+            0x04 calldataload // [x, y, denominator]
+            MUL_DIV_DOWN()
+            0x00 mstore
+            0x20 0x00 return
+        }
+
+        #define macro TEST_3() = takes(0) returns(0) {
+            0x44 calldataload // [denominator]
+            0x24 calldataload // [y, denominator]
+            0x04 calldataload // [x, y, denominator]
+            MUL_DIV_DOWN()
+            0x00 mstore
+            0x20 0x00 return
+        }
+
+        #define macro MAIN() = takes (0) returns (0) {
+            0x00 calldataload 0xE0 shr
+            dup1 __FUNC_SIG(test1) eq test_one jumpi
+            dup1 __FUNC_SIG(test2) eq test_two jumpi
+            dup1 __FUNC_SIG(test3) eq test_three jumpi
+
+            test_one:
+                TEST_1()
+            test_two:
+                TEST_2()
+            test_three:
+                TEST_3()
+        }
+    "#;
+
+    // Parse tokens
+    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::<Vec<Token>>();
+    let mut parser = Parser::new(tokens, None);
+
+    // Parse the AST
+    let mut contract = parser.parse().unwrap();
+
+    // Derive storage pointers
+    contract.derive_storage_pointers();
+
+    // Instantiate Codegen
+    let cg = Codegen::new();
+
+    // The codegen instance should have no artifact
+    assert!(cg.artifact.is_none());
+
+    // Have the Codegen create the runtime bytecode
+    let rbytes = Codegen::generate_main_bytecode(&contract).unwrap();
+    // Churn
+    let mut cg = Codegen::new();
+    let artifact =
+        cg.churn(Arc::clone(&Arc::new(FileSource::default())), vec![], &rbytes, "").unwrap();
+    assert_eq!(artifact.bytecode, String::from("60ad8060093d393df360003560e01c8063075900201461002757806319715c0d1461004457806327902d6914610061575b60443560243560043561003b92919061007e565b60005260206000f35b60443560243560043561005892919061007e565b60005260206000f35b60443560243560043561007592919061007e565b60005260206000f35b828282026000521515908015906000510483141716156100a457506000510460016100aa575b60006000fd5b9056"));
+}
+
+#[test]
+fn test_nested_function() {
+    let source: &str = r#"
+        #define function test1(uint256,uint256,uint256) pure returns(uint256)
+        #define function test2(uint256,uint256,uint256) pure returns(uint256)
+        #define function test3(uint256,uint256,uint256) pure returns(uint256)
+
+        #define fn MUL_DIV_DOWN() = takes (3) returns (1) {
+            // Input stack:      [x, y, denominator]
+            dup3              // [denominator, x, y, denominator]
+            dup3              // [y, denominator, x, y, denominator]
+            dup3              // [x, y, denominator, x, y, denominator]
+
+            mul               // [x * y, denominator, x, y, denominator]
+            0x00 mstore       // [denominator, x, y, denominator]
+
+            iszero iszero     // [denominator != 0, x, y, denominator]
+
+            swap1             // [x, denominator != 0, y, denominator]
+            dup1              // [x, x, denominator != 0, y, denominator]
+            iszero            // [x == 0, x, denominator != 0, y, denominator]
+
+            swap1             // [x, x == 0, denominator != 0, y, denominator]
+            0x00 mload        // [x * y, x, x == 0, denominator != 0, y, denominator]
+            div               // [(x * y) / x, x == 0, denominator != 0, y, denominator]
+
+            dup4              // [y, (x * y) / x, x == 0, denominator != 0, y, denominator]
+            eq                // [y == (x * y) / x, x == 0, denominator != 0, y, denominator]
+            or                // [y == (x * y) / x | x == 0, denominator != 0, y, denominator]
+            and               // [(y == (x * y) / x | x == 0) & denominator != 0, y, denominator]
+
+            iszero fail jumpi // Revert if (y == (x * y) / x | x == 0) & denominator != 0 is not satisfied
+
+            pop               // [denominator]
+            0x00 mload        // [x * y, denominator]
+            div               // [(x * y) / denominator]
+
+            0x01 finish jumpi
+
+            fail:
+                0x00 0x00 revert
+            finish:
+            // Return stack:     [(x * y) / denominator]
+        }
+
+        #define fn MUL_DIV_DOWN_2() = takes (3) returns (1) {
+            MUL_DIV_DOWN()
+        }
+
+        #define macro TEST_1() = takes(0) returns(0) {
+            0x44 calldataload // [denominator]
+            0x24 calldataload // [y, denominator]
+            0x04 calldataload // [x, y, denominator]
+            MUL_DIV_DOWN_2()
+            0x00 mstore
+            0x20 0x00 return
+        }
+
+        #define macro MAIN() = takes (0) returns (0) {
+            0x00 calldataload 0xE0 shr
+            dup1 __FUNC_SIG(test1) eq test_one jumpi
+
+            test_one:
+                TEST_1()
+        }
+    "#;
+
+    // Parse tokens
+    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::<Vec<Token>>();
+    let mut parser = Parser::new(tokens, None);
+
+    // Parse the AST
+    let mut contract = parser.parse().unwrap();
+
+    // Derive storage pointers
+    contract.derive_storage_pointers();
+
+    // Instantiate Codegen
+    let cg = Codegen::new();
+
+    // The codegen instance should have no artifact
+    assert!(cg.artifact.is_none());
+
+    // Have the Codegen create the runtime bytecode
+    let rbytes = Codegen::generate_main_bytecode(&contract).unwrap();
+    // Churn
+    let mut cg = Codegen::new();
+    let artifact =
+        cg.churn(Arc::clone(&Arc::new(FileSource::default())), vec![], &rbytes, "").unwrap();
+    assert_eq!(artifact.bytecode, String::from("606b8060093d393df360003560e01c80630759002014610011575b60443560243560043561002592919061005d565b60005260206000f35b82828202600052151590801590600051048314171615610054575060005104600161005a575b60006000fd5b90565b61006892919061002e565b9056"));
+}
diff --git a/huff_core/tests/gen_artifact.rs b/huff_core/tests/gen_artifact.rs
index a1d4e82..eab2f3b 100644
--- a/huff_core/tests/gen_artifact.rs
+++ b/huff_core/tests/gen_artifact.rs
@@ -31,7 +31,7 @@ fn test_missing_constructor() {
     };
 
     // Instantiate a new compiler
-    let compiler = Compiler::new(Arc::new(vec![]), None, None, false, false);
+    let compiler = Compiler::new(Arc::new(vec![]), None, None, None, false, false);
 
     // Generate the compile artifact
     let arc_source = Arc::new(full_source);
@@ -76,7 +76,8 @@ fn test_missing_constructor_with_inputs() {
     };
 
     // Instantiate a new compiler
-    let compiler = Compiler::new(Arc::new(vec![]), None, Some(vec!["0".to_string()]), false, false);
+    let compiler =
+        Compiler::new(Arc::new(vec![]), None, Some(vec!["0".to_string()]), None, false, false);
 
     // Generate the compile artifact
     let arc_source = Arc::new(full_source);
diff --git a/huff_core/tests/parser_errors.rs b/huff_core/tests/parser_errors.rs
index dd8a150..18831bd 100644
--- a/huff_core/tests/parser_errors.rs
+++ b/huff_core/tests/parser_errors.rs
@@ -1,7 +1,6 @@
 use huff_lexer::*;
 use huff_parser::*;
 use huff_utils::prelude::*;
-use rand::prelude::*;
 
 #[test]
 fn test_invalid_macro_statement() {
@@ -96,7 +95,7 @@ fn test_invalid_definition() {
                 ParserError {
                     kind: ParserErrorKind::InvalidDefinition(TokenKind::Ident("test".to_string())),
                     hint: Some(
-                        "Definition must be one of: `function`, `event`, `constant`, or `macro`."
+                        "Definition must be one of: `function`, `event`, `constant`, `macro`, or `fn`."
                             .to_string()
                     ),
                     spans: AstSpan(vec![Span {
diff --git a/huff_lexer/tests/keywords.rs b/huff_lexer/tests/keywords.rs
index f235a66..5f9554d 100644
--- a/huff_lexer/tests/keywords.rs
+++ b/huff_lexer/tests/keywords.rs
@@ -34,6 +34,38 @@ fn parses_macro_keyword() {
     assert!(lexer.eof);
 }
 
+#[test]
+fn parses_fn_keyword() {
+    let source = "#define fn";
+    let flattened_source = FullFileSource { source, file: None, spans: vec![] };
+    let mut lexer = Lexer::new(flattened_source);
+
+    // Define Identifier first
+    let tok = lexer.next();
+    let unwrapped = tok.unwrap().unwrap();
+    let define_span = Span::new(0..7, None);
+    assert_eq!(unwrapped, Token::new(TokenKind::Define, define_span.clone()));
+    assert_eq!(lexer.current_span().deref(), &define_span);
+
+    // The next token should be the whitespace
+    let tok = lexer.next();
+    let unwrapped = tok.unwrap().unwrap();
+    let whitespace_span = Span::new(7..8, None);
+    assert_eq!(unwrapped, Token::new(TokenKind::Whitespace, whitespace_span.clone()));
+    assert_eq!(lexer.current_span().deref(), &whitespace_span);
+
+    // Lastly we should parse the fn keyword
+    let tok = lexer.next();
+    let unwrapped = tok.unwrap().unwrap();
+    let macro_span = Span::new(8..10, None);
+    assert_eq!(unwrapped, Token::new(TokenKind::Fn, macro_span.clone()));
+    assert_eq!(lexer.current_span().deref(), &macro_span);
+
+    // We covered the whole source
+    assert_eq!(lexer.current_span().end, source.len());
+    assert!(lexer.eof);
+}
+
 #[test]
 fn parses_function_keyword() {
     let source = "#define function";
@@ -270,6 +302,7 @@ fn parses_function_type_keywords() {
 fn parses_function_definition_with_keyword_name() {
     let key_words = [
         "macro",
+        "fn",
         "function",
         "constant",
         "takes",
@@ -332,6 +365,7 @@ fn parses_function_definition_with_keyword_name() {
 fn parses_label_with_keyword_name() {
     let key_words = [
         "macro",
+        "fn",
         "function",
         "constant",
         "takes",
@@ -389,6 +423,7 @@ fn parses_label_with_keyword_name() {
 fn parses_function_with_keyword_name() {
     let key_words = [
         "macro",
+        "fn",
         "function",
         "constant",
         "takes",
@@ -437,6 +472,7 @@ fn parses_function_with_keyword_name() {
 fn parses_function_with_keyword_name_in_macro() {
     let key_words = [
         "macro",
+        "fn",
         "function",
         "constant",
         "takes",
@@ -536,6 +572,7 @@ fn parses_keyword_arbitrary_whitespace() {
     // Macro, constant, and function keywords first- they are all preceded by "#define"
     let key_words = [
         ("macro", TokenKind::Macro),
+        ("fn", TokenKind::Fn),
         ("constant", TokenKind::Constant),
         ("function", TokenKind::Function),
     ];
diff --git a/huff_lexer/tests/labels.rs b/huff_lexer/tests/labels.rs
index d34a371..ab6cb94 100644
--- a/huff_lexer/tests/labels.rs
+++ b/huff_lexer/tests/labels.rs
@@ -19,3 +19,22 @@ fn parse_label() {
     );
     assert_eq!(tokens.get(tokens.len() - 4).unwrap().kind, TokenKind::Colon);
 }
+
+#[test]
+fn parse_label_with_opcode_name() {
+    let source =
+        "#define macro HELLO_WORLD() = takes(3) returns(0) {\n0x00 mstore\n 0x01 0x02 add cool_label_return_swap1_mload:\n0x01\n}";
+    let flattened_source = FullFileSource { source, file: None, spans: vec![] };
+    let lexer = Lexer::new(flattened_source);
+    let tokens = lexer
+        .into_iter()
+        .map(|x| x.unwrap())
+        .filter(|x| !matches!(x.kind, TokenKind::Whitespace))
+        .collect::<Vec<Token>>();
+
+    assert_eq!(
+        tokens.get(tokens.len() - 5).unwrap().kind,
+        TokenKind::Label("cool_label_return_swap1_mload".to_string())
+    );
+    assert_eq!(tokens.get(tokens.len() - 4).unwrap().kind, TokenKind::Colon);
+}
diff --git a/huff_parser/tests/constant.rs b/huff_parser/tests/constant.rs
index 4653438..e7f4126 100644
--- a/huff_parser/tests/constant.rs
+++ b/huff_parser/tests/constant.rs
@@ -12,7 +12,7 @@ fn test_parses_free_storage_pointer_constant() {
     let contract = parser.parse().unwrap();
     assert_eq!(parser.current_token.kind, TokenKind::Eof);
 
-    let fsp_constant = contract.constants[0].clone();
+    let fsp_constant = contract.constants.borrow()[0].clone();
     assert_eq!(
         fsp_constant,
         ConstantDefinition {
@@ -44,7 +44,7 @@ fn test_parses_literal_constant() {
         str_to_bytes32("8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925");
 
     // Check Literal
-    let fsp_constant = contract.constants[0].clone();
+    let fsp_constant = contract.constants.borrow()[0].clone();
     assert_eq!(
         fsp_constant,
         ConstantDefinition {
diff --git a/huff_parser/tests/labels.rs b/huff_parser/tests/labels.rs
index c162434..c73a86b 100644
--- a/huff_parser/tests/labels.rs
+++ b/huff_parser/tests/labels.rs
@@ -160,6 +160,7 @@ fn multiline_labels() {
             // "}"
             Span { start: 158, end: 159, file: None },
         ]),
+        outlined: false,
     };
     assert_eq!(macro_definition.name, md_expected.name);
     assert_eq!(macro_definition.parameters, md_expected.parameters);
diff --git a/huff_parser/tests/macro.rs b/huff_parser/tests/macro.rs
index dcf45e9..34aacae 100644
--- a/huff_parser/tests/macro.rs
+++ b/huff_parser/tests/macro.rs
@@ -36,6 +36,7 @@ fn empty_macro() {
             Span { start: 50, end: 51, file: None },
             Span { start: 51, end: 52, file: None },
         ]),
+        outlined: false,
     };
     assert_eq!(macro_definition, expected);
     assert_eq!(parser.current_token.kind, TokenKind::Eof);
@@ -102,6 +103,7 @@ fn macro_with_simple_body() {
             Span { start: 75, end: 78, file: None },
             Span { start: 79, end: 80, file: None },
         ]),
+        outlined: false,
     };
     assert_eq!(macro_definition, expected);
     assert_eq!(parser.current_token.kind, TokenKind::Eof);
@@ -280,6 +282,7 @@ fn macro_with_arg_calls() {
             Span { start: 1028, end: 1029, file: None },
             Span { start: 1055, end: 1056, file: None },
         ]),
+        outlined: false,
     };
     assert_eq!(macro_definition, expected);
     assert_eq!(parser.current_token.kind, TokenKind::Eof);
@@ -453,6 +456,7 @@ fn macro_labels() {
             Span { start: 205, end: 211, file: None },
             Span { start: 216, end: 217, file: None },
         ]),
+        outlined: false,
     };
     assert_eq!(macro_definition, expected);
     assert_eq!(parser.current_token.kind, TokenKind::Eof);
@@ -595,6 +599,7 @@ fn macro_invocation_with_arg_call() {
             Span { start: 173, end: 179, file: None },
             Span { start: 184, end: 185, file: None },
         ]),
+        outlined: false,
     };
     assert_eq!(macro_definition, expected);
     assert_eq!(parser.current_token.kind, TokenKind::Eof);
@@ -663,6 +668,115 @@ fn macro_with_builtin_fn_call() {
             Span { start: 81, end: 82, file: None },
             Span { start: 87, end: 88, file: None },
         ]),
+        outlined: false,
+    };
+    assert_eq!(macro_definition, expected);
+    assert_eq!(parser.current_token.kind, TokenKind::Eof);
+}
+
+// Outlined macros (functions) are parsed the same way as inlined macros, so there should be no
+// difference besides the spans as well as the outlined flag.
+#[test]
+fn empty_outlined_macro() {
+    let source = "#define fn HELLO_WORLD() = takes(0) returns(4) {}";
+    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::<Vec<Token>>();
+    let mut parser = Parser::new(tokens, None);
+
+    // Grab the first macro
+    let macro_definition = parser.parse().unwrap().macros[0].clone();
+    let expected = MacroDefinition {
+        name: "HELLO_WORLD".to_string(),
+        parameters: vec![],
+        statements: vec![],
+        takes: 0,
+        returns: 4,
+        span: AstSpan(vec![
+            Span { start: 0, end: 7, file: None },
+            Span { start: 8, end: 10, file: None },
+            Span { start: 11, end: 22, file: None },
+            Span { start: 22, end: 23, file: None },
+            Span { start: 23, end: 24, file: None },
+            Span { start: 25, end: 26, file: None },
+            Span { start: 27, end: 32, file: None },
+            Span { start: 32, end: 33, file: None },
+            Span { start: 33, end: 34, file: None },
+            Span { start: 34, end: 35, file: None },
+            Span { start: 36, end: 43, file: None },
+            Span { start: 43, end: 44, file: None },
+            Span { start: 44, end: 45, file: None },
+            Span { start: 45, end: 46, file: None },
+            Span { start: 47, end: 48, file: None },
+            Span { start: 48, end: 49, file: None },
+        ]),
+        outlined: true,
+    };
+    assert_eq!(macro_definition, expected);
+    assert_eq!(parser.current_token.kind, TokenKind::Eof);
+}
+
+#[test]
+fn outlined_macro_with_simple_body() {
+    let source = "#define fn HELLO_WORLD() = takes(3) returns(0) {\n0x00 mstore\n 0x01 0x02 add\n}";
+    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::<Vec<Token>>();
+    let mut parser = Parser::new(tokens, None);
+
+    // Grab the first macro
+    let macro_definition = parser.parse().unwrap().macros[0].clone();
+    let expected = MacroDefinition {
+        name: "HELLO_WORLD".to_string(),
+        parameters: vec![],
+        statements: vec![
+            Statement {
+                ty: StatementType::Literal(str_to_bytes32("00")),
+                span: AstSpan(vec![Span { start: 51, end: 53, file: None }]),
+            },
+            Statement {
+                ty: StatementType::Opcode(Opcode::Mstore),
+                span: AstSpan(vec![Span { start: 54, end: 60, file: None }]),
+            },
+            Statement {
+                ty: StatementType::Literal(str_to_bytes32("01")),
+                span: AstSpan(vec![Span { start: 64, end: 66, file: None }]),
+            },
+            Statement {
+                ty: StatementType::Literal(str_to_bytes32("02")),
+                span: AstSpan(vec![Span { start: 69, end: 71, file: None }]),
+            },
+            Statement {
+                ty: StatementType::Opcode(Opcode::Add),
+                span: AstSpan(vec![Span { start: 72, end: 75, file: None }]),
+            },
+        ],
+        takes: 3,
+        returns: 0,
+        span: AstSpan(vec![
+            Span { start: 0, end: 7, file: None },
+            Span { start: 8, end: 10, file: None },
+            Span { start: 11, end: 22, file: None },
+            Span { start: 22, end: 23, file: None },
+            Span { start: 23, end: 24, file: None },
+            Span { start: 25, end: 26, file: None },
+            Span { start: 27, end: 32, file: None },
+            Span { start: 32, end: 33, file: None },
+            Span { start: 33, end: 34, file: None },
+            Span { start: 34, end: 35, file: None },
+            Span { start: 36, end: 43, file: None },
+            Span { start: 43, end: 44, file: None },
+            Span { start: 44, end: 45, file: None },
+            Span { start: 45, end: 46, file: None },
+            Span { start: 47, end: 48, file: None },
+            Span { start: 51, end: 53, file: None },
+            Span { start: 54, end: 60, file: None },
+            Span { start: 64, end: 66, file: None },
+            Span { start: 69, end: 71, file: None },
+            Span { start: 72, end: 75, file: None },
+            Span { start: 76, end: 77, file: None },
+        ]),
+        outlined: true,
     };
     assert_eq!(macro_definition, expected);
     assert_eq!(parser.current_token.kind, TokenKind::Eof);
diff --git a/huff_parser/tests/storage_pointer_derivation.rs b/huff_parser/tests/storage_pointer_derivation.rs
index c3598e4..f1769a3 100644
--- a/huff_parser/tests/storage_pointer_derivation.rs
+++ b/huff_parser/tests/storage_pointer_derivation.rs
@@ -15,7 +15,7 @@ fn derives_storage_pointers() {
     assert_eq!(parser.current_token.kind, TokenKind::Eof);
 
     // Ensure that the constant definitions were parsed correctly
-    let fsp_constant = contract.constants[0].clone();
+    let fsp_constant = contract.constants.borrow()[0].clone();
     assert_eq!(
         fsp_constant,
         ConstantDefinition {
@@ -31,7 +31,7 @@ fn derives_storage_pointers() {
         }
     );
 
-    let fsp_constant = contract.constants[1].clone();
+    let fsp_constant = contract.constants.borrow()[1].clone();
     assert_eq!(
         fsp_constant,
         ConstantDefinition {
@@ -47,7 +47,7 @@ fn derives_storage_pointers() {
         }
     );
 
-    let num_constant = contract.constants[2].clone();
+    let num_constant = contract.constants.borrow()[2].clone();
     assert_eq!(
         num_constant,
         ConstantDefinition {
@@ -67,7 +67,13 @@ fn derives_storage_pointers() {
     contract.derive_storage_pointers();
 
     // Ensure that the storage pointers were set for the FSP constants in the AST
-    assert_eq!(contract.constants[0].value, ConstVal::FreeStoragePointer(FreeStoragePointer));
-    assert_eq!(contract.constants[1].value, ConstVal::FreeStoragePointer(FreeStoragePointer));
-    assert_eq!(contract.constants[2].value, ConstVal::Literal(str_to_bytes32("a57B")));
+    assert_eq!(
+        contract.constants.borrow()[0].value,
+        ConstVal::FreeStoragePointer(FreeStoragePointer)
+    );
+    assert_eq!(
+        contract.constants.borrow()[1].value,
+        ConstVal::FreeStoragePointer(FreeStoragePointer)
+    );
+    assert_eq!(contract.constants.borrow()[2].value, ConstVal::Literal(str_to_bytes32("a57B")));
 }
diff --git a/huff_parser/tests/table.rs b/huff_parser/tests/table.rs
index 5f04835..11008ae 100644
--- a/huff_parser/tests/table.rs
+++ b/huff_parser/tests/table.rs
@@ -43,7 +43,7 @@ fn table_with_no_body() {
 #[test]
 fn table_with_body() {
     // TODO: Code tables are not yet supported
-    let table_kinds = [(TokenKind::JumpTable, "96"), (TokenKind::JumpTablePacked, "06")];
+    let table_kinds = [(TokenKind::JumpTable, "60"), (TokenKind::JumpTablePacked, "06")];
 
     for (kind, expected_size) in table_kinds {
         let source = &format!(