Expand description
§Codegen
Bytecode Generation Module for the Huff Language.
§Architecture
The huff_codegen module exposes a few main bytecode generation functions. It is expected that both the huff_lexer and huff_parser are executed before huff_codegen to produce a valid abstract syntax tree (Contract in our case) that huff_codegen can walk.
Once the AST (Contract) is produced, Codegen can be used to produce the MAIN and CONSTRUCTOR bytecode.
The generate_main_bytecode function takes a reference of Contract and produces a bytecode String on success or a CodegenError on failure.
Likewise, the generate_constructor_bytecode function takes a reference of Contract and produces a bytecode String on success or a CodegenError on failure.
churn takes the generated CONSTRUCTOR and MAIN macros’ bytecode and produces an Artifact containing:
- The file source: Artifact.file
- The deployed bytecode: Artifact.deployed
- The runtime bytecode: Artifact.runtime
- The contract ABI: Artifact.abi
§Usage
Below we showcase generating a compile artifact from compiled bytecode using huff_codegen.
use huff_neo_codegen::*;
use huff_neo_utils::prelude::*;
use std::sync::Arc;
use std::cell::RefCell;
use std::rc::Rc;
fn example() {
// Instantiate an empty Codegen
let mut cg = Codegen::new();
assert!(cg.ast.is_none());
assert!(cg.artifact.is_none());
// ERC20 Bytecode
let main_bytecode = "5f3560e01c8063a9059cbb1461004757806340c10f19146100d757806370a082311461014157806318160ddd1461015c578063095ea7b314610166578063dd62ed3e1461017d575b600435336024358160016000526000602001526040600020548082116100d3578190038260016000526000602001526040600020558281906001600052600060200152604060002054018360016000526000602001526040600020555f527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205fa360015f5260205ff35b5f5ffd5b5f5433146100e3575f5ffd5b6004355f60243582819060016000526000602001526040600020540183600160005260006020015260406000205580600254016002555f527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205fa35b60043560016000526000602001526040600020545f5260205ff35b6002545f5260205ff35b602435600435336000526000602001526040600020555b6024356004356000526000602001526040600020545f5260205ff3";
let constructor_bytecode = "335f55";
let inputs = vec![];
let churn_res = cg.churn(&EVMVersion::default(), Arc::new(FileSource::default()), inputs, main_bytecode, constructor_bytecode, false, None, None, false);
// Validate the output bytecode
assert_eq!(churn_res.unwrap().bytecode, "335f5561019980600d3d393df35f3560e01c8063a9059cbb1461004757806340c10f19146100d757806370a082311461014157806318160ddd1461015c578063095ea7b314610166578063dd62ed3e1461017d575b600435336024358160016000526000602001526040600020548082116100d3578190038260016000526000602001526040600020558281906001600052600060200152604060002054018360016000526000602001526040600020555f527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205fa360015f5260205ff35b5f5ffd5b5f5433146100e3575f5ffd5b6004355f60243582819060016000526000602001526040600020540183600160005260006020015260406000205580600254016002555f527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205fa35b60043560016000526000602001526040600020545f5260205ff35b6002545f5260205ff35b602435600435336000526000602001526040600020555b6024356004356000526000602001526040600020545f5260205ff3".to_lowercase());
// Write the compile artifact out to a file
// cg.export("./output.json");
}Let’s say you have a Contract instance with a simple MAIN macro. You can generate the main macro bytecode using the generate_main_bytecode function.
use huff_neo_codegen::*;
use huff_neo_utils::prelude::*;
use std::collections::HashSet;
use std::sync::{Arc, Mutex};
use indexmap::IndexMap;
fn example() {
// Mock contract with a main macro
let contract = Contract {
flattened_source: Some(String::new()),
source_files: vec![],
source_map: vec![],
macros: IndexMap::from([("MAIN".to_string(),
MacroDefinition {
name: "MAIN".to_string(),
decorator: None,
parameters: vec![],
statements: vec![
Statement {
ty: StatementType::Literal(str_to_bytes32("00")),
span: AstSpan(vec![]),
},
Statement {
ty: StatementType::Opcode(Opcode::Calldataload),
span: AstSpan(vec![]),
},
Statement {
ty: StatementType::Literal(str_to_bytes32("E0")),
span: AstSpan(vec![]),
},
Statement {
ty: StatementType::Opcode(Opcode::Shr),
span: AstSpan(vec![]),
}
],
takes: 0,
returns: 0,
span: AstSpan(vec![]),
outlined: false,
test: false,
}
)]),
invocations: vec![],
imports: vec![],
constants: Arc::new(Mutex::new(vec![])),
errors: vec![],
functions: vec![],
events: vec![],
tables: vec![],
labels: HashSet::new(),
runtime_size: None,
};
// Generate the main bytecode
let main_bytecode: String = Codegen::generate_main_bytecode(&EVMVersion::default(), &contract, None, false).unwrap();
// Validate the output bytecode
assert_eq!(main_bytecode, "5f3560e01c");
}Similarly, once you have a Contract instance with a simple CONSTRUCTOR macro definition. You can generate the constructor/creation bytecode using the generate_constructor_bytecode function.
use huff_neo_codegen::*;
use huff_neo_utils::prelude::*;
use std::collections::HashSet;
use std::sync::{Arc, Mutex};
use indexmap::IndexMap;
fn example() {
// Mock contract with a constructor macro
let contract = Contract {
flattened_source: Some(String::new()),
source_files: vec![],
source_map: vec![],
macros: IndexMap::from([("CONSTRUCTOR".to_string(),
MacroDefinition {
name: "CONSTRUCTOR".to_string(),
decorator: None,
parameters: vec![],
statements: vec![
Statement {
ty: StatementType::Literal(str_to_bytes32("00")),
span: AstSpan(vec![]),
},
Statement {
ty: StatementType::Opcode(Opcode::Calldataload),
span: AstSpan(vec![]),
},
Statement {
ty: StatementType::Literal(str_to_bytes32("E0")),
span: AstSpan(vec![]),
},
Statement {
ty: StatementType::Opcode(Opcode::Shr),
span: AstSpan(vec![]),
}
],
takes: 0,
returns: 0,
span: AstSpan(vec![]),
outlined: false,
test: false,
}
)]),
invocations: vec![],
imports: vec![],
constants: Arc::new(Mutex::new(vec![])),
errors: vec![],
functions: vec![],
events: vec![],
tables: vec![],
labels: HashSet::new(),
runtime_size: None,
};
// Generate the constructor bytecode
let (constructor_bytecode, has_custom_bootstrap): (String, bool) = Codegen::generate_constructor_bytecode(&EVMVersion::default(), &contract, None, false).unwrap();
// Validate the output bytecode
assert_eq!(constructor_bytecode, "5f3560e01c");
assert_eq!(has_custom_bootstrap, false);
}Modules§
- irgen 🔒
Structs§
- Codegen
- Compiles Huff contracts into EVM bytecode.
- Constructor
Macro Bytecode - Constructor macro bytecode produced by phase 1, prior to table resolution and bootstrap insertion.
- Filled
Jumps - Bytecode whose jumps are resolved as far as their labels are known
- Main
Bytecode Output - Output of
Codegen::generate_main_bytecode_with_sourcemap. - Resized
Bytecode - Bytecode whose span-dependent pushes have stable widths
- Table
Bytecode Output - Output of
Codegen::gen_table_bytecode.
Enums§
- Resize
Mode 🔒 - Direction in which span-dependent pushes (jump targets, self-referencing
__codesize) may change
Constants§
- CODESIZE_
MAX_ 🔒ITERATIONS - Maximum number of codegen passes of MAIN or the constructor used to converge
__codesize. - JUMP_
RELAXATION_ 🔒MAX_ ITERATIONS - Maximum number of iterations allowed for the jump relaxation optimization algorithm.
- MAX_
MACRO_ 🔒RECURSION_ DEPTH - Maximum macro recursion depth allowed during compilation.
Functions§
- args_
start_ 🔒span - The source span of the first
__argsstart()call in generated code, if there is one - converge_
codesizes 🔒 - Runs codegen passes until the values a pass depends on are the ones it produces, starting
from
initial, and returns the output of the last pass - current_
offset 🔒 - Translates a code boundary from original segment offsets to the segment offsets after resizing.
- remap_
jump_ 🔒table - Moves jump table entries to the offsets their jumps have after resizing
- remap_
offset 🔒 - Translates a segment offset to its position after resizing; offsets without a segment stay put
Type Aliases§
- Offset
Mapping - Mapping from segment offsets before resizing span-dependent pushes to the offsets after it