Skip to content

Conversation

@micahscopes
Copy link
Collaborator

No description provided.

Introduce codegen backend abstraction for multi-target support:
- Backend trait with compile() method returning Yul or Bytecode
- YulBackend wrapping existing emit_module_yul
- SonatinaBackend stub (not yet implemented)
- --backend flag for fe check command (yul/sonatina)
Implement the core structure for lowering Fe MIR to Sonatina IR:
- Add sonatina-ir and sonatina-triple dependencies
- Create sonatina module with ModuleLowerer for translating functions
- Implement type mapping (all EVM values are I256)
- Implement basic terminator lowering (Return, Goto, Branch)
- Wire up SonatinaBackend to call the new compile_module function

The lowering currently handles function declarations, block structure,
and control flow. MIR instruction lowering is stubbed for Phase 2.
Add comprehensive MIR-to-Sonatina IR lowering:
- Lower synthetic values (integers, booleans, bytes) to I256 immediates
- Lower unary operations (not, minus, bitnot, plus)
- Lower binary operations (arithmetic, comparison, logical)
- Lower basic rvalues (ZeroInit, Value, Load)
- Lower terminators with proper value resolution
- Add num-bigint dependency for BigUint handling
- Use EVM-specific division/modulo instructions (EvmSdiv, EvmSmod)

Instruction lowering is scaffolded with TODOs for:
- Function calls, memory allocation, intrinsics
- Aggregate initialization, stores, discriminants
- Control flow phi nodes
- Match MIR IntrinsicOp::Mload shape
- Preserve local mapping when lowering terminators
- Align shift operand order with EVM semantics
- Use unsigned div/mod and big-endian bytes immediates
- Use Osaka target triple and wire EvmExp
Add proper dispatching for load/store operations based on address space:
- Memory: Mload/Mstore
- Storage: EvmSload/EvmSstore
- TransientStorage: EvmTload/EvmTstore
- Calldata: EvmCalldataLoad (read-only)

Places with projections are rejected for now.
Add name_map to track symbol name → FuncRef mappings, enabling calls
to be resolved by their monomorphized symbol names. Lowers both regular
arguments and effect arguments, emitting Sonatina Call instructions.
Add the full compilation pipeline from Fe MIR to EVM bytecode:

- Add sonatina-codegen dependency for EVM code generation
- Create Sonatina Objects with runtime sections after lowering
- Wire up EvmObjectBackend and compile_object in SonatinaBackend
- Extract bytecode from compiled runtime section artifact

The Sonatina backend now produces actual EVM bytecode instead of
returning an error stub.
ControlFlowResult values are converted to Local values during MIR
lowering (in lower_if). If one reaches codegen, it indicates a bug
in the MIR lowering pipeline, so return an error instead of silently
returning zero.
Add support for projections in load/store operations:

- Add lower_place_address function that walks projection paths
- Handle Field, VariantField, Discriminant, and Index projections
- Use slot-based offsets for storage, byte-based for memory
- Support both constant and dynamic array indices
- Thread database and layout through function signatures

This enables the Sonatina backend to handle struct field access,
enum variant access, and array indexing for both memory and storage.
Address reviewer feedback on the Sonatina backend:

- Fix call signature mismatch: include effect_param_locals in signatures
  and argument mapping
- Fix call return type: track callee return types instead of hardcoding I256
- Fix PlaceRef projections: use lower_place_address for full path computation
- Make InitAggregate/SetDiscriminant return Unsupported instead of silent no-op
- Fix Terminator::Unreachable to emit EvmInvalid instead of clean return
- Fix ValueOrigin::FuncItem to error instead of silently returning 0
- Use body.place_address_space() instead of custom helper with wrong default
- Thread db/target_layout through lower_value for projection address computation
- Add TODO documentation for SSA phi nodes, entry block, and object semantics

Also add docs/backend-equivalence-testing.md proposal for differential
testing between Yul and Sonatina backends using revm and Halmos.
Add from_word and to_word conversions matching Yul emitter semantics:

- from_word (after load):
  - bool: convert to 0 or 1 via Ne comparison
  - unsigned sub-word: Trunc then Zext to mask high bits
  - signed sub-word: Trunc then Sext for sign extension

- to_word (before store):
  - bool: iszero(iszero(value)) to normalize
  - unsigned sub-word: Trunc then Zext to mask
  - signed: pass through (already sign-extended)

Deep-copy for by-ref aggregates is not yet implemented and returns
an Unsupported error. This matches the Yul emitter's recursive
field-by-field copy logic which needs separate implementation.
Add emit_module_sonatina_ir() to export human-readable Sonatina IR text,
and create snapshot tests for all 74 codegen fixtures. Snapshots are
stored in fixtures/sonatina_ir/ to avoid conflicting with Yul snapshots.

This provides visibility into the IR structure between MIR and bytecode,
making it easier to debug lowering issues and review generated IR.

72 fixtures generate IR successfully; 2 are skipped due to missing
intrinsics (create2, child contract init).
Contract runtime entrypoints (dispatch functions) are now executed directly
by the EVM without a wrapper function. They emit `evm_stop` instead of
internal `Return` since the EVM needs an explicit halt opcode.

Simple test files still use the wrapper approach to preserve internal
function call semantics for functions that return values.

Also adds optional EVM instruction tracing via FE_TRACE_EVM env var.
Replace manual Solidity-style memory allocation (mload/mstore 0x40) with
Sonatina's EvmMalloc instruction. This delegates memory management to
Sonatina's codegen, avoiding conflicts with its stack frame handling.

Note: EvmMalloc lowering is not yet implemented in Sonatina's EVM backend,
so tests requiring memory allocation are temporarily disabled. Once Sonatina
implements EvmMalloc, these tests should pass.
///
/// For simple test files (no explicit contract): A wrapper function calls the entry
/// and then does `evm_stop`, preserving internal function call semantics.
fn create_objects(&mut self) -> Result<(), LowerError> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • all functions reachable via calls from %entry will be automatically included. %include is for including functions that don't appear in the call graph
  • to find the contract runtime and ini functions, you should look for the contract_init(Foo)/contract_runtime(Foo) attributes (or whatever they are). Look elsewhere in MIR/codegen for how these are found, and share code.
  • contract init objects aren't currently being created

Sonatina's evm-codegen branch now implements EvmMalloc lowering,
so struct_init and match_enum_with_data fixtures compile successfully.
Comment on lines +14 to +16
sonatina-ir = { path = "../../../sonatina/crates/ir" }
sonatina-triple = { path = "../../../sonatina/crates/triple" }
sonatina-codegen = { path = "../../../sonatina/crates/codegen" }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Point to github plz

Remove FE_TEST_SONATINA gating in contract-harness tests, fix StorageMap scratch memory for Sonatina runs, and add a build script to rerun codegen tests when fixtures change.
Add Sonatina test-module emission for , including runtime bytecode plumbing and necessary layout/codegen adjustments.
- Add `fe test` flags for Sonatina backend and debug outputs (EVM trace, symtab, stackify)\n- Write traces/symtabs to files via `--debug-dir` for easier human debugging\n- Adjust stdlib allocator to avoid backend-reserved memory\n- Update codegen snapshots and add Sonatina operand collection regression tests
- Sort code-region reachability and embeds for deterministic section layout\n- Sort cycle-detection traversal for stable errors\n- Ignore local stackify/trace artifacts
Set `SONATINA_DISABLE_TRANSIENT_MALLOC=1` for the `fe test --backend sonatina` runner to avoid transient-allocation overlap corrupting heap-backed values during Fe tests.
Only default `SONATINA_DISABLE_TRANSIENT_MALLOC=1` when not already set, so developers can repro transient-malloc issues with `SONATINA_DISABLE_TRANSIENT_MALLOC=0`.
Expose Sonatina's transient malloc analysis trace via `fe test` flags and support writing it to `--debug-dir/sonatina_transient_malloc_trace.txt`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants