Skip to content
This repository was archived by the owner on Aug 17, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion codegen/src/codegen/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
local::{LocalSlot, LocalSlotType, Locals},
masm::MacroAssembler,
validator::ValidateThenVisit,
wasm::Env,
wasm::{Env, ToLSBytes},
Buffer, Error, Result,
};
use opcodes::ShangHai as OpCode;
Expand Down Expand Up @@ -60,6 +60,7 @@ impl Function {
// codegen.masm.increment_sp(1)?;
tracing::debug!("<External function>");
codegen.masm._jumpdest()?;
codegen.hydrate_tuple_params()?;
} else {
// Mock the stack frame for the callee function
//
Expand All @@ -72,6 +73,34 @@ impl Function {
Ok(codegen)
}

/// Copy tuple ABI parameters from calldata into EVM memory.
fn hydrate_tuple_params(&mut self) -> Result<()> {
let Some(abi) = self.abi.clone() else {
return Ok(());
};

let mut calldata_slot = 0;
for (local_index, input) in abi.inputs.iter().enumerate() {
if let Some(fields) = input.ty.tuple_field_offsets() {
let base = (self.env.reserved() + local_index as u32) as usize * 0x20;

for (field_offset, field_slot) in fields.into_iter().rev() {
let calldata_offset = 4 + (calldata_slot + field_slot) * 32;
let memory_offset = base + field_offset;

self.masm.push(&calldata_offset.to_ls_bytes())?;
self.masm._calldataload()?;
self.masm.push(&memory_offset.to_ls_bytes())?;
self.masm._mstore()?;
}
}

calldata_slot += input.ty.calldata_slots();
}

Ok(())
}

/// Emit function locals
///
/// 1. the function parameters.
Expand Down
51 changes: 38 additions & 13 deletions codegen/src/masm/memory.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,82 @@
//! Memory Instructions

use crate::{MacroAssembler, Result};
use crate::{wasm::ToLSBytes, MacroAssembler, Result};
use wasmparser::MemArg;

impl MacroAssembler {
/// Load n bytes to extend self as another number type.
///
/// Just for adapting the WASM instructions, this method makes
/// no sense for EVM since all of the numbers as U256.
pub(crate) fn _load(&mut self) -> Result<()> {
Ok(())
pub(crate) fn _load(&mut self, arg: MemArg) -> Result<()> {
self._load_bytes(arg, 4)
}

/// Load 8 bytes.
pub(crate) fn _load64(&mut self, arg: MemArg) -> Result<()> {
self._load_bytes(arg, 8)
}

fn _load_bytes(&mut self, arg: MemArg, bytes: usize) -> Result<()> {
if arg.offset > 0 {
self.push(&arg.offset.to_ls_bytes())?;
self._add()?;
}

self._mload()?;
self.mask_low_bytes(bytes)
}

fn mask_low_bytes(&mut self, bytes: usize) -> Result<()> {
if bytes >= 32 {
return Ok(());
}

self.push(&vec![0xff; bytes])?;
self._and()
}

/// Load 1 byte to extend self as another number type.
///
/// Just for adapting the WASM instructions, this method makes
/// no sense for EVM since all of the numbers as U256.
pub(crate) fn _load8(&mut self) -> Result<()> {
Ok(())
pub(crate) fn _load8(&mut self, arg: MemArg) -> Result<()> {
self._load_bytes(arg, 1)
}

/// Load 2 bytes to extend self as another number type.
///
/// Just for adapting the WASM instructions, this method makes
/// no sense for EVM since all of the numbers as U256.
pub(crate) fn _load16(&mut self) -> Result<()> {
Ok(())
pub(crate) fn _load16(&mut self, arg: MemArg) -> Result<()> {
self._load_bytes(arg, 2)
}

/// Load 4 bytes to extend self as another number type.
///
/// Just for adapting the WASM instructions, this method makes
/// no sense for EVM since all of the numbers as U256.
pub(crate) fn _load32(&mut self) -> Result<()> {
Ok(())
pub(crate) fn _load32(&mut self, arg: MemArg) -> Result<()> {
self._load_bytes(arg, 4)
}

/// Store n bytes in memory.
pub fn _store(&mut self) -> Result<()> {
pub fn _store(&mut self, _: MemArg) -> Result<()> {
todo!()
}

/// Wrap self to i8 and store 1 byte
pub fn _store8(&mut self) -> Result<()> {
pub fn _store8(&mut self, _: MemArg) -> Result<()> {
todo!()
}

/// Wrap self to i16 and store 2 bytes
pub fn _store16(&mut self) -> Result<()> {
pub fn _store16(&mut self, _: MemArg) -> Result<()> {
todo!()
}

/// Wrap self to i32 and store 4 bytes
pub fn _store32(&mut self) -> Result<()> {
pub fn _store32(&mut self, _: MemArg) -> Result<()> {
todo!()
}

Expand Down
32 changes: 31 additions & 1 deletion codegen/src/visitor/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,17 @@ impl Function {
fn _local_get_calldata(&mut self, local_index: usize) -> Result<()> {
let mut offset = self.locals.offset_of(local_index)?;
if self.abi.is_some() {
offset = (4 + local_index * 32).to_ls_bytes().to_vec().into();
if self.is_tuple_abi_param(local_index) {
offset = ((self.env.reserved() + local_index as u32) * 0x20)
.to_ls_bytes()
.to_vec()
.into();
self.masm.push(&offset)?;
return Ok(());
}

let calldata_slot = self.abi_calldata_slot(local_index);
offset = (4 + calldata_slot * 32).to_ls_bytes().to_vec().into();
}

self.masm.push(&offset)?;
Expand All @@ -54,6 +64,26 @@ impl Function {
Ok(())
}

fn abi_calldata_slot(&self, local_index: usize) -> usize {
let Some(abi) = &self.abi else {
return local_index;
};

abi.inputs
.iter()
.take(local_index)
.map(|input| input.ty.calldata_slots())
.sum()
}

fn is_tuple_abi_param(&self, local_index: usize) -> bool {
self.abi
.as_ref()
.and_then(|abi| abi.inputs.get(local_index))
.and_then(|input| input.ty.tuple_field_offsets())
.is_some()
}

/// Local get for variables.
fn _local_get_var(&mut self, local_index: usize) -> Result<()> {
tracing::debug!("Local get variable: {local_index}");
Expand Down
54 changes: 53 additions & 1 deletion codegen/src/visitor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,65 @@ macro_rules! impl_visit_operator {

/// Implement arithmetic operators for types.
macro_rules! map_wasm_operators {
(@basic i32, load, load $arg:ident: MemArg) => {
fn visit_i32_load(&mut self, $arg: MemArg) -> Self::Output {
trace!("i32.load");

let before = self.masm.buffer().len();
self.masm._load32($arg)?;

let instr = self.masm.buffer()[before..].to_vec();
self.backtrace.push(instr);

Ok(())
}
};
(@basic i64, load, load $arg:ident: MemArg) => {
fn visit_i64_load(&mut self, $arg: MemArg) -> Self::Output {
trace!("i64.load");

let before = self.masm.buffer().len();
self.masm._load64($arg)?;

let instr = self.masm.buffer()[before..].to_vec();
self.backtrace.push(instr);

Ok(())
}
};
(@basic f32, load, load $arg:ident: MemArg) => {
fn visit_f32_load(&mut self, $arg: MemArg) -> Self::Output {
trace!("f32.load");

let before = self.masm.buffer().len();
self.masm._load32($arg)?;

let instr = self.masm.buffer()[before..].to_vec();
self.backtrace.push(instr);

Ok(())
}
};
(@basic f64, load, load $arg:ident: MemArg) => {
fn visit_f64_load(&mut self, $arg: MemArg) -> Self::Output {
trace!("f64.load");

let before = self.masm.buffer().len();
self.masm._load64($arg)?;

let instr = self.masm.buffer()[before..].to_vec();
self.backtrace.push(instr);

Ok(())
}
};
(@basic $ty:tt, $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
paste! {
fn [< visit_ $ty _ $wasm >](&mut self $(,$arg: $argty),*) -> Self::Output {
trace!("{}.{}", stringify!($ty), stringify!($evm));

let before = self.masm.buffer().len();
self.masm.[< _ $evm >]()?;
self.masm.[< _ $evm >]($($arg),*)?;

let instr = self.masm.buffer()[before..].to_vec();
self.backtrace.push(instr);
Expand Down
Loading
Loading