Skip to content

Conversation

@cburgdorf
Copy link
Collaborator

@cburgdorf cburgdorf commented Nov 19, 2025

This builds on top of #1159. It connects our current codegen pipeline that could previously only produce YUL to the solc-runner that takes the yul and compiles it into bytecode.

This also introduces a testing harness that takes Fe source, compiles it, deploys it on an EVM instance and lets us do transactions to interact with the deployed contract to make assertions.

Obviously this side steps quite a few bigger issues (e.g. ad hoc hardcoded dispatcher) but it is really valuable to be able to generate binary code and execute it. This already exposed quite a few issues with the generated YUL and hence helps me to drive the codegen work forward.

It also does a bit of a cleanup for the YUL emitter code.

@cburgdorf cburgdorf marked this pull request as ready for review November 19, 2025 22:27
@cburgdorf cburgdorf changed the title Mvp contract compilation + testing harness Contract to bytecode compilation + E2E testing harness Nov 19, 2025
@cburgdorf cburgdorf requested review from Copilot and sbillig November 19, 2025 22:28
Copilot finished reviewing on behalf of cburgdorf November 20, 2025 11:28
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces a complete end-to-end compilation pipeline that takes Fe source code through to executable EVM bytecode. The main additions are a Solc runner that compiles Yul to bytecode and a contract testing harness built on revm for executing and testing contracts.

Key Changes:

  • Added solc-runner and contract-harness crates to enable bytecode compilation and EVM execution
  • Introduced new intrinsics (calldataload, return_data) and Dispatcher trait for contract runtime support
  • Refactored Yul emitter into modular components (function, statements, control flow, expressions)
  • Added ReturnData terminator to MIR for raw memory returns

Reviewed Changes

Copilot reviewed 64 out of 65 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
library/core/src/lib.fe Exports new Dispatcher trait and additional intrinsics for contract runtime
library/core/src/intrinsic.fe Adds calldataload and return_data intrinsics for contract ABI handling
library/core/src/dispatcher.fe Defines minimal Dispatcher trait interface for contract entry points
crates/solc-runner/src/lib.rs New crate that invokes solc to compile Yul into bytecode
crates/solc-runner/Cargo.toml Dependencies for solc runner (contains invalid edition)
crates/contract-harness/src/lib.rs Complete testing harness using revm for contract execution
crates/contract-harness/Cargo.toml Dependencies for contract testing harness
crates/codegen/src/yul/emitter/*.rs Refactored emitter split into focused modules (function, statements, control_flow, expr, util, module)
crates/codegen/src/yul/state.rs Updated to use Rc<Cell<usize>> for shared local counter across cloned states
crates/mir/src/lower.rs Adds ensure_const_expr_values and handles ReturnData terminator
crates/mir/src/ir.rs Adds Calldataload and ReturnData intrinsic opcodes and new terminator variant
crates/mir/src/hash.rs Hash implementation for new ReturnData terminator
crates/mir/src/monomorphize.rs Adds method name prefixing for impl blocks
crates/hir-analysis/src/ty/ty_check/*.rs Caches callable information to avoid redundant trait resolution
crates/codegen/tests/fixtures/*.fe New test fixtures for contract dispatch and intrinsics
.github/workflows/main.yml CI setup to download and cache solc for tests

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if matches!(value_data.origin, ValueOrigin::Call(..)) {
docs.push(YulDoc::line(format!("pop({lowered})")));
} else {
docs.push(YulDoc::line(lowered));
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

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

[nitpick] The variable v0 is not being used in the returned value for this intrinsic. After evaluating the call_expr in line 209, the code checks if it's a ValueOrigin::Call and wraps it with pop(), but for all other value origins it just returns the lowered expression as a line. This means that for non-call expression statements (line 212), we're emitting a bare expression as a statement, which in Yul would just be a value that gets discarded. This seems intentional but could lead to confusing Yul output.

Suggested change
docs.push(YulDoc::line(lowered));
// Do not emit a bare expression as a statement; value is discarded.
// Optionally, emit a comment for clarity:
// docs.push(YulDoc::line(format("// discarded value: {lowered}")));

Copilot uses AI. Check for mistakes.
Comment on lines +332 to +337
let line = match intr.op {
IntrinsicOp::Mstore => format!("mstore({}, {})", args[0], args[1]),
IntrinsicOp::Mstore8 => format!("mstore8({}, {})", args[0], args[1]),
IntrinsicOp::Sstore => format!("sstore({}, {})", args[0], args[1]),
IntrinsicOp::ReturnData => format!("return({}, {})", args[0], args[1]),
_ => unreachable!(),
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

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

Missing handling for IntrinsicOp::ReturnData case. The function only handles Mstore, Mstore8, and Sstore in the match statement (lines 333-335), but based on the IR changes, ReturnData is also a non-value-returning intrinsic that should be handled here. The unreachable!() at line 337 would be hit if ReturnData intrinsic is processed as a statement.

Copilot uses AI. Check for mistakes.
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.

1 participant