Skip to main content

Crate huff_neo_codegen

Crate huff_neo_codegen 

Source
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:

§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.
ConstructorMacroBytecode
Constructor macro bytecode produced by phase 1, prior to table resolution and bootstrap insertion.
FilledJumps
Bytecode whose jumps are resolved as far as their labels are known
MainBytecodeOutput
Output of Codegen::generate_main_bytecode_with_sourcemap.
ResizedBytecode
Bytecode whose span-dependent pushes have stable widths
TableBytecodeOutput
Output of Codegen::gen_table_bytecode.

Enums§

ResizeMode 🔒
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§

OffsetMapping
Mapping from segment offsets before resizing span-dependent pushes to the offsets after it