diff --git a/Cargo.lock b/Cargo.lock index c52adb5d9..a40007073 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -391,6 +391,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -485,9 +491,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" dependencies = [ "libc", ] @@ -682,6 +688,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + [[package]] name = "enumn" version = "0.1.12" @@ -1020,16 +1032,6 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.20" @@ -1218,29 +1220,6 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "smallvec", - "windows-targets", -] - [[package]] name = "paste" version = "1.0.14" @@ -1281,6 +1260,17 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "postcard" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" +dependencies = [ + "cobs", + "embedded-io", + "serde", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1405,15 +1395,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "regex" version = "1.10.2" @@ -1614,12 +1595,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "scratch" version = "1.0.7" @@ -1874,7 +1849,7 @@ checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall", "rustix", "windows-sys", ] @@ -2347,7 +2322,11 @@ dependencies = [ name = "zabi" version = "0.1.4" dependencies = [ + "hex", + "postcard", + "serde", "sha3", + "thiserror", ] [[package]] @@ -2396,13 +2375,10 @@ dependencies = [ name = "zink-codegen" version = "0.1.4" dependencies = [ - "hex", - "once_cell", - "parking_lot", "proc-macro2", "quote", - "sha3", "syn 2.0.38", + "zabi", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 72d3c9e8b..f7f8654c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ indexmap = "2.0.0" once_cell = "1.18.0" parking_lot = "0.12.1" paste = "1.0.13" +postcard = { version = "1.0.8", default-features = false } proc-macro2 = "1.0.69" quote = "1.0.33" revm = "3.5.0" diff --git a/codegen/abi/Cargo.toml b/codegen/abi/Cargo.toml index fb8852285..e058668ea 100644 --- a/codegen/abi/Cargo.toml +++ b/codegen/abi/Cargo.toml @@ -10,4 +10,8 @@ license.workspace = true repository.workspace = true [dependencies] +hex.workspace = true +postcard = { workspace = true, default-features = false, features = [ "use-std" ] } +serde = { workspace = true, features = [ "derive" ] } sha3.workspace = true +thiserror.workspace = true diff --git a/codegen/abi/src/lib.rs b/codegen/abi/src/lib.rs index 136b2c7b7..bbbc48521 100644 --- a/codegen/abi/src/lib.rs +++ b/codegen/abi/src/lib.rs @@ -1,6 +1,10 @@ //! Utils for generating of zink ABI +pub use self::result::{Error, Result}; +use serde::{Deserialize, Serialize}; use sha3::{Digest, Keccak256}; +mod result; + /// Generate a keccak hash of the input (sha3) pub fn keccak256(input: &[u8]) -> [u8; 32] { let mut hasher = Keccak256::new(); @@ -15,3 +19,53 @@ pub fn selector(input: &[u8]) -> [u8; 4] { selector } + +/// Function ABI. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Abi { + /// Function name. + pub name: String, + /// Function inputs. + pub inputs: Vec, +} + +impl Abi { + /// Get function signature. + pub fn signature(&self) -> String { + self.name.clone() + "(" + &self.inputs.join(",") + ")" + } + + /// Get function selector. + pub fn selector(&self) -> [u8; 4] { + let sig = self.signature(); + let mut selector = [0u8; 4]; + selector.copy_from_slice(&keccak256(sig.as_bytes())[..4]); + + selector + } + + /// Parse ABI from bytes. + pub fn from_bytes(bytes: &[u8]) -> Result { + postcard::from_bytes(bytes).map_err(Into::into) + } + + /// Decode ABI form hex string. + pub fn from_hex(hex: &str) -> Result { + Self::from_bytes(&hex::decode(hex)?) + } + + /// Decode ABI form hex string. + pub fn from_hex_bytes(bytes: &[u8]) -> Result { + Self::from_hex(&String::from_utf8_lossy(bytes)) + } + + /// Convert ABI to hex string. + pub fn to_hex(&self) -> Result { + self.to_bytes().map(hex::encode) + } + + /// Convert ABI to bytes. + pub fn to_bytes(&self) -> Result> { + postcard::to_stdvec(&self).map_err(Into::into) + } +} diff --git a/codegen/abi/src/result.rs b/codegen/abi/src/result.rs new file mode 100644 index 000000000..41e579feb --- /dev/null +++ b/codegen/abi/src/result.rs @@ -0,0 +1,15 @@ +//! Abi results + +/// ABI error +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// Failed to encode or decode with postcard. + #[error(transparent)] + Postcard(#[from] postcard::Error), + /// Failed to decode from hex. + #[error(transparent)] + Hex(#[from] hex::FromHexError), +} + +/// ABI result +pub type Result = std::result::Result; diff --git a/codegen/src/abi.rs b/codegen/src/abi.rs index 5b8362071..aa39b7822 100644 --- a/codegen/src/abi.rs +++ b/codegen/src/abi.rs @@ -92,6 +92,7 @@ macro_rules! offset { offset! { (usize, 8), + (u64, 8), (i64, 8), (i32, 4), (u32, 4), diff --git a/codegen/src/asm.rs b/codegen/src/asm.rs index be7081afe..f1c5f876d 100644 --- a/codegen/src/asm.rs +++ b/codegen/src/asm.rs @@ -6,7 +6,7 @@ use crate::{Buffer, Error, Result}; use opcodes::{for_each_shanghai_operator, OpCode as _, ShangHai as OpCode}; /// Low level assembler implementation for EVM. -#[derive(Default)] +#[derive(Default, Clone)] pub struct Assembler { /// Buffer of the assembler. buffer: Buffer, @@ -97,7 +97,7 @@ impl Assembler { /// Mock the stack input and output for checking /// the stack usages. pub fn emit_op(&mut self, opcode: OpCode) -> Result<()> { - tracing::trace!("stack length: {:?}", self.sp); + // tracing::trace!("stack length: {:?}", self.sp); tracing::trace!("emit opcode: {:?}", opcode); self.decrement_sp(opcode.stack_in() as u8)?; self.emit(opcode.into()); diff --git a/codegen/src/backtrace.rs b/codegen/src/backtrace.rs index e9df1ab23..e5a4aae97 100644 --- a/codegen/src/backtrace.rs +++ b/codegen/src/backtrace.rs @@ -1,32 +1,36 @@ //! Backtrace support for the code generation. +use std::collections::BTreeMap; /// Backtrace implementation for the code generation. /// -/// TODO: full implementation #21 -#[derive(Default)] +/// TODO: full implementation (#21) +#[derive(Debug, Default)] pub struct Backtrace { - /// The length of each operand. - len: Vec, + /// Compiled instructions. + /// + /// TODO: Transform this into Opcodes. (#21) + instrs: BTreeMap>, } impl Backtrace { - /// Pushes a new operand to the backtrace. - pub fn push(&mut self, len: usize) { - self.len.push(len); + /// Pushes a new instruction set to the backtrace. + pub fn push(&mut self, bytes: impl AsRef<[u8]>) { + self.instrs.insert(self.instrs.len(), bytes.as_ref().into()); } - /// Pops the last operand from the backtrace. - pub fn pop(&mut self) -> usize { - self.len.pop().unwrap_or_default() + /// Pops the last instruction from the backtrace. + pub fn pop(&mut self) -> Vec { + self.instrs.pop_last().unwrap_or_default().1 } - pub fn popn(&mut self, n: usize) -> usize { - let mut r: Vec = Default::default(); + /// Pop the last `n` operands from the backtrace. + pub fn popn(&mut self, n: usize) -> Vec> { + let mut r: Vec> = Default::default(); while r.len() < n { r.push(self.pop()) } - r.into_iter().sum() + r } } diff --git a/codegen/src/code/func.rs b/codegen/src/code/func.rs new file mode 100644 index 000000000..b827f0029 --- /dev/null +++ b/codegen/src/code/func.rs @@ -0,0 +1,45 @@ +//! External Function for the code section. +use opcodes::ShangHai as OpCode; + +trait OpCodesToBytes { + fn to_bytes(self) -> Vec; +} + +impl OpCodesToBytes for &[OpCode] { + fn to_bytes(self) -> Vec { + [&[OpCode::JUMPDEST], self] + .concat() + .iter() + .map(|op| (*op).into()) + .collect() + } +} + +/// External function in code section. +#[derive(PartialEq, Eq, Debug, Clone, Hash)] +pub struct ExtFunc { + /// Stack input. + pub stack_out: u8, + /// Stack output. + pub stack_in: u8, + /// The bytecode of the external function. + pub bytecode: Vec, +} + +impl ExtFunc { + /// Function select. + pub fn select() -> Self { + Self { + stack_in: 2, + stack_out: 1, + bytecode: [ + OpCode::POP, + OpCode::PUSH1, + OpCode::Data(0x06), + OpCode::ADD, + OpCode::JUMP, + ] + .to_bytes(), + } + } +} diff --git a/codegen/src/jump/code.rs b/codegen/src/code/mod.rs similarity index 81% rename from codegen/src/jump/code.rs rename to codegen/src/code/mod.rs index ada77a739..a83ddd864 100644 --- a/codegen/src/jump/code.rs +++ b/codegen/src/code/mod.rs @@ -1,14 +1,16 @@ //! Table for the code section. -use crate::Func; +pub use func::ExtFunc; use indexmap::IndexMap; +mod func; + /// Code section for EVM. #[derive(Default, Debug)] pub struct Code { offset: usize, /// Function table. - funcs: IndexMap, + funcs: IndexMap, } impl Code { @@ -21,7 +23,7 @@ impl Code { } /// Get the functions in the code section. - pub fn funcs(&self) -> Vec { + pub fn funcs(&self) -> Vec { self.funcs.keys().cloned().collect() } @@ -34,12 +36,12 @@ impl Code { } /// Add a function to the code section. - pub fn try_add_func(&mut self, func: Func) { + pub fn try_add_func(&mut self, func: ExtFunc) { if self.funcs.contains_key(&func) { return; } - let bytecode = func.bytecode(); + let bytecode = func.bytecode.clone(); let len = bytecode.len(); self.funcs.insert(func, self.offset); self.offset += len; @@ -51,7 +53,7 @@ impl Code { } /// Get the offset of a function. - pub fn offset_of(&self, func: &Func) -> Option { + pub fn offset_of(&self, func: &ExtFunc) -> Option { self.funcs.get(func).and_then(|i| (*i).try_into().ok()) } @@ -60,7 +62,7 @@ impl Code { let mut code = Vec::new(); for func in self.funcs.keys() { tracing::debug!("add function to code section: {:?}", func); - code.extend(func.bytecode()); + code.extend(func.bytecode.clone()); } code } diff --git a/codegen/src/codegen.rs b/codegen/src/codegen.rs index ab0f684df..5729874dd 100644 --- a/codegen/src/codegen.rs +++ b/codegen/src/codegen.rs @@ -59,7 +59,7 @@ impl CodeGen { // STACK: PC + params codegen.masm.increment_sp(1 + params_count)?; codegen.masm._jumpdest()?; - codegen.masm.shift_pc(params_count, true)?; + codegen.masm.shift_stack(params_count, true)?; } Ok(codegen) diff --git a/codegen/src/dispatcher.rs b/codegen/src/dispatcher.rs index 35a1720aa..0410a41a3 100644 --- a/codegen/src/dispatcher.rs +++ b/codegen/src/dispatcher.rs @@ -1,11 +1,16 @@ //! Code generator for EVM dispatcher. -use crate::{DataSet, Error, Exports, Imports, JumpTable, MacroAssembler, Result}; +use crate::{ + code::ExtFunc, DataSet, Error, Exports, Imports, JumpTable, MacroAssembler, Result, ToLSBytes, +}; use std::{ collections::BTreeMap, ops::{Deref, DerefMut}, }; -use wasmparser::{FuncValidator, FunctionBody, Operator, ValidatorResources}; +use wasmparser::{ + FuncType, FuncValidator, FunctionBody, Operator, ValidatorResources, WasmModuleResources, +}; +use zabi::Abi; /// Function with validator. pub struct Function<'f> { @@ -15,6 +20,26 @@ pub struct Function<'f> { pub body: FunctionBody<'f>, } +impl Function<'_> { + /// Get function index. + pub fn index(&self) -> u32 { + self.validator.index() + } + + /// Get the function signature. + pub fn sig(&self) -> Result { + let func_index = self.validator.index(); + let sig = self + .validator + .resources() + .type_of_function(func_index) + .ok_or(Error::InvalidFunctionSignature)? + .clone(); + + Ok(sig) + } +} + /// Functions with indexes. #[derive(Default)] pub struct Functions<'f>(BTreeMap>); @@ -69,12 +94,13 @@ impl<'f> Functions<'f> { } /// Code generator for EVM dispatcher. -#[derive(Default)] -pub struct Dispatcher { +pub struct Dispatcher<'d> { /// Code buffer pub asm: MacroAssembler, /// Module exports pub exports: Exports, + /// Module functions + pub funcs: &'d Functions<'d>, /// Module imports pub imports: Imports, /// Module data @@ -83,7 +109,19 @@ pub struct Dispatcher { pub table: JumpTable, } -impl Dispatcher { +impl<'d> Dispatcher<'d> { + /// Create dispatcher with functions. + pub fn new(funcs: &'d Functions<'d>) -> Self { + Self { + asm: Default::default(), + exports: Default::default(), + funcs, + imports: Default::default(), + data: Default::default(), + table: Default::default(), + } + } + /// Set exports for the dispatcher. pub fn exports(&mut self, exports: Exports) -> &mut Self { self.exports = exports; @@ -104,15 +142,6 @@ impl Dispatcher { /// Query exported function from selector. fn query_func(&self, name: &str) -> Result { - let name = { - let splits = name.split('(').collect::>(); - if splits.len() < 2 { - return Err(Error::InvalidSelector); - } - - splits[0] - }; - for (index, export) in self.exports.iter() { if export.name == name { return Ok(*index); @@ -122,8 +151,8 @@ impl Dispatcher { Err(Error::FuncNotImported(name.into())) } - /// Emit selector to buffer - fn emit_selector(&mut self, selector: &Function<'_>, last: bool) -> Result<()> { + /// Load function ABI. + fn load_abi(&mut self, selector: &Function<'_>) -> Result { let mut reader = selector.body.get_operators_reader()?; // Get data offset. @@ -143,35 +172,141 @@ impl Dispatcher { else { return Err(Error::InvalidSelector); }; + if !self.imports.is_emit_abi(index) { return Err(Error::FuncNotImported("emit_abi".into())); } - let data = self.data.load(offset, length as usize)?; - let name = String::from_utf8_lossy(&data); - let selector = zabi::selector(name.as_bytes()); + Abi::from_hex_bytes(&self.data.load(offset, length as usize)?).map_err(Into::into) + } + + /// Emit return of ext function. + fn ext_return(&mut self, sig: &FuncType) -> Result<()> { + self.asm.increment_sp(1)?; + let asm = self.asm.clone(); + + { + self.asm.main_return(sig.results())?; + } + + let bytecode = { + let jumpdest = vec![0x5b]; + let ret = self.asm.buffer()[asm.buffer().len()..].to_vec(); + [jumpdest, ret].concat() + }; + + *self.asm = asm; + let ret = ExtFunc { + bytecode, + stack_in: 0, + stack_out: 0, + }; + self.table.ext(self.asm.pc_offset(), ret); + Ok(()) + } - tracing::debug!("Emitting selector {:?} for function: {}", selector, name); + // Process to the selected function. + // + // 1. drop selector. + // 2. load calldata to stack. + // 3. jump to the callee function. + fn process(&mut self, sig: &FuncType) -> Result<()> { + self.asm.increment_sp(1)?; + let asm = self.asm.clone(); + let len = sig.params().len() as u8; + + { + // TODO: check the safety of this. + // + // [ callee, ret, selector ] -> [ selector, callee, ret ] + self.asm.shift_stack(2, false)?; + // [ selector, callee, ret ] -> [ callee, ret ] + self.asm._drop()?; + // [ callee, ret ] -> [ param * len, callee, ret ] + for p in (0..len).rev() { + let offset = 4 + p * 32; + self.asm.push(&offset.to_ls_bytes())?; + self.asm._calldataload()?; + } - let func = self.query_func(&name)?; + // [ param * len, callee, ret ] -> [ ret, param * len, callee ] + self.asm.shift_stack(len + 1, false)?; + // [ ret, param * len, callee ] -> [ callee, ret, param * len ] + self.asm.shift_stack(len + 1, false)?; + self.asm._jump()?; + } + + let bytecode = { + let jumpdest = vec![0x5b]; + let ret = self.asm.buffer()[asm.buffer().len()..].to_vec(); + [jumpdest, ret].concat() + }; + *self.asm = asm; + let ret = ExtFunc { + bytecode, + stack_in: len, + stack_out: 1, + }; + self.table.ext(self.asm.pc_offset(), ret); + Ok(()) + } + + /// Emit selector to buffer. + fn emit_selector(&mut self, selector: &Function<'_>, last: bool) -> Result<()> { + let abi = self.load_abi(selector)?; + let selector_bytes = abi.selector(); + + tracing::debug!( + "Emitting selector {:?} for function: {}", + selector_bytes, + abi.name + ); + + let func = self.query_func(&abi.name)?; + let sig = self + .funcs + .get(&func) + .ok_or(Error::FuncNotFound(func))? + .sig()?; + + // Jump to the end of the current function. + // + // TODO: detect the bytes of the position. (#157) + self.ext_return(&sig)?; + + // Prepare the `PC` of the callee function. + { + // TODO: remove this (#160) + self.asm._jumpdest()?; + self.asm.increment_sp(1)?; + self.table.call(self.asm.pc_offset(), func); + } if !last { - self.asm._dup1()?; + self.asm._dup3()?; + } else { + self.asm._swap2()?; } - self.asm.push(&selector)?; + self.asm.push(&selector_bytes)?; self.asm._eq()?; - self.asm.increment_sp(1)?; - self.table.call(self.asm.pc_offset(), func); + self.process(&sig)?; self.asm._jumpi()?; + if !last { + // drop the PC of the previous callee function. + self.asm._drop()?; + // drop the PC of the previous callee function preprocessor. + self.asm._drop()?; + } + Ok(()) } /// Emit compiled code to the given buffer. pub fn finish(mut self, selectors: Functions<'_>, table: &mut JumpTable) -> Result> { if selectors.is_empty() { - return Ok(self.asm.buffer().into()); + return Err(Error::SelectorNotFound); } self.asm._push0()?; @@ -181,7 +316,7 @@ impl Dispatcher { let mut len = selectors.len(); for (_, func) in selectors.iter() { - self.emit_selector(func, len == 0)?; + self.emit_selector(func, len == 1)?; len -= 1; } diff --git a/codegen/src/func.rs b/codegen/src/func.rs deleted file mode 100644 index 0e5206657..000000000 --- a/codegen/src/func.rs +++ /dev/null @@ -1,152 +0,0 @@ -//! Built-in functions for EVM -use crate::{Error, MacroAssembler, Result}; -use opcodes::ShangHai as OpCode; - -/// Selects one of its first two operands based on whether -/// its third operand is zero or not. -const SELECT: [OpCode; 6] = [ - OpCode::JUMPDEST, - OpCode::POP, - OpCode::PUSH1, - OpCode::Data(0x06), - OpCode::ADD, - OpCode::JUMP, -]; - -/// Function `sload` from EVM which is not available in WASM. -const SLOAD: [OpCode; 7] = [ - OpCode::JUMPDEST, - OpCode::SLOAD, - OpCode::SWAP1, - OpCode::PUSH1, - OpCode::Data(0x05), - OpCode::ADD, - OpCode::JUMP, -]; - -/// Function `sstore` from EVM which is not available in WASM. -const SSTORE: [OpCode; 6] = [ - OpCode::JUMPDEST, - OpCode::SSTORE, - OpCode::PUSH1, - OpCode::Data(0x05), - OpCode::ADD, - OpCode::JUMP, -]; - -/// EVM built-in function. -#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] -pub enum Func { - /// Run function select. - Select, - /// Run function sload. - Sload, - /// Run function sstore. - Sstore, - /// Run function log0. - Log0, - /// Run function log1. - Log1, - /// Run function log2. - Log2, - /// Run function log3. - Log3, - /// Run function log4. - Log4, - - // zinkc helper functions - EmitABI, -} - -impl Func { - /// Stack input size. - pub fn stack_in(&self) -> u8 { - match self { - Self::Select => 3, - Self::Sload => 1, - Self::Sstore => 2, - Self::Log0 => 2, - Self::Log1 => 4, - Self::Log2 => 6, - Self::Log3 => 8, - Self::Log4 => 10, - _ => 0, - } - } - - /// Stack output size. - pub fn stack_out(&self) -> u8 { - match self { - Self::Select => 1, - Self::Sload => 1, - Self::Sstore => 0, - Self::Log0 => 0, - Self::Log1 => 0, - Self::Log2 => 0, - Self::Log3 => 0, - Self::Log4 => 0, - _ => 0, - } - } - - /// Pre-processing for the function. - pub fn prelude(&self, masm: &mut MacroAssembler) -> Result<()> { - match self { - Self::Select => { - masm._pc()?; - masm._swap2()?; - masm._swap1() - } - Self::Sstore => masm._swap1(), - _ => Ok(()), - } - } - - /// Get the bytecode of the function. - pub fn bytecode(&self) -> Vec { - match self { - Self::Select => SELECT.to_vec(), - Self::Sload => SLOAD.to_vec(), - Self::Sstore => SSTORE.to_vec(), - _ => unimplemented!("not implemented for {:?}", self), - } - .into_iter() - .map(|op| op.into()) - .collect() - } - - /// If this function should be embedded in - /// the main code. - /// - /// TODO: return `false` for functions that - /// are necessary to just stay in the code - /// section #109 - pub fn is_embedded(&self) -> bool { - true - } -} - -impl TryFrom<(&str, &str)> for Func { - type Error = Error; - - fn try_from(import: (&str, &str)) -> Result { - let (module, name) = import; - // NOTE: `select` is not external call - // so we don't need to check process it - // here - match import { - ("evm", "sload") => Ok(Self::Sload), - ("evm", "sstore") => Ok(Self::Sstore), - ("evm", "log0") => Ok(Self::Log0), - ("evm", "log1") => Ok(Self::Log1), - ("evm", "log2") => Ok(Self::Log2), - ("evm", "log3") => Ok(Self::Log3), - ("evm", "log4") => Ok(Self::Log4), - ("zinkc", "emit_abi") => Ok(Self::EmitABI), - _ => { - tracing::error!("Failed to load host function: {:?}", import); - Err(Error::HostFuncNotFound(module.into(), name.into())) - } - } - } -} diff --git a/codegen/src/import.rs b/codegen/src/import.rs index 456b54a73..a44eb6789 100644 --- a/codegen/src/import.rs +++ b/codegen/src/import.rs @@ -4,46 +4,13 @@ use std::{ ops::{Deref, DerefMut}, }; -use crate::{Error, MacroAssembler, Result}; -use opcodes::ShangHai as OpCode; - -/// Selects one of its first two operands based on whether -/// its third operand is zero or not. -const SELECT: [OpCode; 6] = [ - OpCode::JUMPDEST, - OpCode::POP, - OpCode::PUSH1, - OpCode::Data(0x06), - OpCode::ADD, - OpCode::JUMP, -]; - -/// Function `sload` from EVM which is not available in WASM. -const SLOAD: [OpCode; 7] = [ - OpCode::JUMPDEST, - OpCode::SLOAD, - OpCode::SWAP1, - OpCode::PUSH1, - OpCode::Data(0x05), - OpCode::ADD, - OpCode::JUMP, -]; - -/// Function `sstore` from EVM which is not available in WASM. -const SSTORE: [OpCode; 6] = [ - OpCode::JUMPDEST, - OpCode::SSTORE, - OpCode::PUSH1, - OpCode::Data(0x05), - OpCode::ADD, - OpCode::JUMP, -]; +use crate::{Error, Result}; /// EVM built-in function. #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] pub enum Func { - /// Run function select. - Select, + // EVM functions. + // /// Run function sload. Sload, /// Run function sstore. @@ -59,7 +26,9 @@ pub enum Func { /// Run function log4. Log4, + // // Zinkc helper functions + // /// Emit ABI to the compiler. EmitABI, } @@ -68,7 +37,6 @@ impl Func { /// Stack input size. pub fn stack_in(&self) -> u8 { match self { - Self::Select => 3, Self::Sload => 1, Self::Sstore => 2, Self::Log0 => 2, @@ -83,7 +51,6 @@ impl Func { /// Stack output size. pub fn stack_out(&self) -> u8 { match self { - Self::Select => 1, Self::Sload => 1, Self::Sstore => 0, Self::Log0 => 0, @@ -94,42 +61,6 @@ impl Func { _ => 0, } } - - /// Pre-processing for the function. - pub fn prelude(&self, masm: &mut MacroAssembler) -> Result<()> { - match self { - Self::Select => { - masm._pc()?; - masm._swap2()?; - masm._swap1() - } - Self::Sstore => masm._swap1(), - _ => Ok(()), - } - } - - /// Get the bytecode of the function. - pub fn bytecode(&self) -> Vec { - match self { - Self::Select => SELECT.to_vec(), - Self::Sload => SLOAD.to_vec(), - Self::Sstore => SSTORE.to_vec(), - _ => unimplemented!("not implemented for {:?}", self), - } - .into_iter() - .map(|op| op.into()) - .collect() - } - - /// If this function should be embedded in - /// the main code. - /// - /// TODO: return `false` for functions that - /// are necessary to just stay in the code - /// section #109 - pub fn is_embedded(&self) -> bool { - true - } } impl TryFrom<(&str, &str)> for Func { diff --git a/codegen/src/jump/mod.rs b/codegen/src/jump/mod.rs index e3a9c7af2..e202acc0f 100644 --- a/codegen/src/jump/mod.rs +++ b/codegen/src/jump/mod.rs @@ -1,28 +1,34 @@ //! Jump table implementation. -pub use self::{code::Code, table::JumpTable}; -use crate::Func; +use crate::code::ExtFunc; +pub use table::JumpTable; -mod code; mod pc; mod relocate; mod table; /// Jump types -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum Jump { - /// Jump to the given label, the label here is the original - /// program counter. + /// offset to the program counter. + Offset(u16), + /// Jump to the given label, the label here + /// is the original program counter. Label(u16), /// Jump to function. Func(u32), /// External function. - ExtFunc(Func), + ExtFunc(ExtFunc), } impl Jump { /// If the target is a label. pub fn is_label(&self) -> bool { - matches!(self, Jump::Label(_)) + matches!(self, Jump::Label { .. }) + } + + /// If the target is fixed to offset of the program counter. + pub fn is_offset(&self) -> bool { + matches!(self, Jump::Offset(_)) } } diff --git a/codegen/src/jump/pc.rs b/codegen/src/jump/pc.rs index f9354bbc0..46d09966e 100644 --- a/codegen/src/jump/pc.rs +++ b/codegen/src/jump/pc.rs @@ -74,7 +74,7 @@ impl JumpTable { } } - Ok((k, *v)) + Ok((k, v.clone())) }) .collect::>()?; diff --git a/codegen/src/jump/relocate.rs b/codegen/src/jump/relocate.rs index 8a2e99a81..4f7ded928 100644 --- a/codegen/src/jump/relocate.rs +++ b/codegen/src/jump/relocate.rs @@ -18,7 +18,12 @@ impl JumpTable { tracing::debug!("run relocateion for {jump:?}",); let offset = relocate::offset(pc)?; - relocate::pc(buffer, pc, self.target(&jump)?, offset)?; + let mut target = self.target(&jump)?; + if jump.is_offset() { + target += pc; + } + + relocate::pc(buffer, pc, target, offset)?; self.shift_label_pc(pc, offset)?; } diff --git a/codegen/src/jump/table.rs b/codegen/src/jump/table.rs index e34db59d9..769bfab0b 100644 --- a/codegen/src/jump/table.rs +++ b/codegen/src/jump/table.rs @@ -1,12 +1,10 @@ //! Jump Table -use crate::{ - jump::{Code, Jump}, - Error, Func, Result, -}; +use crate::{code::ExtFunc, jump::Jump, Code, Error, Result}; use std::collections::BTreeMap; /// Jump table implementation. +/// #[derive(Default, Debug)] pub struct JumpTable { /// Jump table. @@ -32,15 +30,15 @@ impl JumpTable { Ok(()) } - /// Register program counter for code section. + /// Register the start of the program counter + /// of the code section. pub fn code_offset(&mut self, offset: u16) { self.code.shift(offset); } /// Register a external function. - pub fn ext(&mut self, pc: u16, func: Func) { - tracing::debug!("register external function: {:?}", func); - self.code.try_add_func(func); + pub fn ext(&mut self, pc: u16, func: ExtFunc) { + self.code.try_add_func(func.clone()); self.jump.insert(pc, Jump::ExtFunc(func)); } @@ -49,6 +47,11 @@ impl JumpTable { self.jump.insert(pc, Jump::Label(label)); } + /// Register a label. + pub fn offset(&mut self, pc: u16, offset: u16) { + self.jump.insert(pc, Jump::Offset(offset)); + } + /// Merge two jump tables. /// /// Merge other jump table into this one, update the program @@ -80,9 +83,10 @@ impl JumpTable { /// Get the target of a jump. pub fn target(&mut self, jump: &Jump) -> Result { match jump { + Jump::Offset(offset) => Ok(*offset), Jump::Label(label) => Ok(*label), Jump::Func(func) => Ok(*self.func.get(func).ok_or(Error::FuncNotFound(*func))?), - Jump::ExtFunc(ext) => Ok(self.code.offset_of(ext).ok_or(Error::ExtNotFound(*ext))?), + Jump::ExtFunc(ext) => Ok(self.code.offset_of(ext).ok_or(Error::ExtFuncNotFound)?), } } } diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 3d99aaa90..69b9baeb0 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -5,13 +5,14 @@ pub use crate::{ abi::{ToLSBytes, Type}, asm::Assembler, + code::Code, codegen::CodeGen, control::{ControlStack, ControlStackFrame, ControlStackFrameType}, data::DataSet, dispatcher::{Dispatcher, Function, Functions}, export::Exports, import::{Func, Imports}, - jump::{Code, JumpTable}, + jump::JumpTable, local::{LocalSlot, Locals}, masm::MacroAssembler, result::{Error, Result}, @@ -21,6 +22,7 @@ use smallvec::SmallVec; pub mod abi; mod asm; mod backtrace; +mod code; mod codegen; mod control; mod data; diff --git a/codegen/src/masm/integer.rs b/codegen/src/masm/integer.rs index b3c4995bf..ff65c2575 100644 --- a/codegen/src/masm/integer.rs +++ b/codegen/src/masm/integer.rs @@ -12,14 +12,20 @@ impl MacroAssembler { /// Push a 32-bit integer value on the stack. pub fn _i32_const(&mut self, value: i32) -> Result<()> { - self.push(value.to_ls_bytes().as_ref())?; - Ok(()) + if value == 0 { + self._push0() + } else { + self.push(value.to_ls_bytes().as_ref()) + } } /// Push a 64-bit integer value on the stack. pub fn _i64_const(&mut self, value: i64) -> Result<()> { - self.push(value.to_ls_bytes().as_ref())?; - Ok(()) + if value == 0 { + self._push0() + } else { + self.push(value.to_ls_bytes().as_ref()) + } } /// Push a 32-bit float value on the stack. diff --git a/codegen/src/masm/mod.rs b/codegen/src/masm/mod.rs index c2963b551..1dc995765 100644 --- a/codegen/src/masm/mod.rs +++ b/codegen/src/masm/mod.rs @@ -13,6 +13,7 @@ mod embed; mod float; mod integer; mod memory; +mod ret; mod stack; /// EVM MacroAssembler. @@ -192,7 +193,7 @@ impl MacroAssembler { /// Shift the program counter to the bottom or the top of the /// parameters. This is used by the callee function for jumping /// back to the caller function. - pub fn shift_pc(&mut self, count: u8, from_top: bool) -> Result<()> { + pub fn shift_stack(&mut self, count: u8, from_top: bool) -> Result<()> { let mut swaps = 0; if from_top { diff --git a/codegen/src/masm/ret.rs b/codegen/src/masm/ret.rs new file mode 100644 index 000000000..2c9626289 --- /dev/null +++ b/codegen/src/masm/ret.rs @@ -0,0 +1,52 @@ +//! Return handlers + +use crate::{Error, MacroAssembler, Result, ToLSBytes}; +use wasmparser::ValType; + +impl MacroAssembler { + /// Return with nothing. + pub(crate) fn handle_empty_return(&mut self) -> Result<()> { + self._push0()?; + self._push0()?; + self.asm._return()?; + + Ok(()) + } + + /// Handle the end of the main function. + pub fn main_return(&mut self, results: &[ValType]) -> Result<()> { + if results.is_empty() { + return self.handle_empty_return(); + } + + let size = self.memory_write(results)?.size; + let offset = + self.mp_offset(|mp| mp.checked_sub(size).ok_or_else(|| Error::InvalidMP(0)))?; + + self.push(&size.to_ls_bytes())?; + self.push(&offset)?; + self.asm._return()?; + Ok(()) + } + + /// Handle the return of a call. + pub fn call_return(&mut self, results: &[ValType]) -> Result<()> { + let len = results.len() as u8; + let sp = self.sp(); + for i in 0..len { + // TODO: arthmetic overflow. + // + // 2 is for PC & self. + self.swap(sp - i - 2)?; + } + + tracing::debug!("cleaning frame stack, target: {}", len + 1); + while self.sp() > len + 1 { + self._drop()?; + } + + // TODO: handle the length of results > u8::MAX. + self.shift_stack(len, false)?; + self._jump() + } +} diff --git a/codegen/src/result.rs b/codegen/src/result.rs index 818741ff6..bbf01eeb7 100644 --- a/codegen/src/result.rs +++ b/codegen/src/result.rs @@ -3,6 +3,9 @@ /// Codegen error #[derive(Debug, thiserror::Error)] pub enum Error { + /// Failed to parse function ABI. + #[error(transparent)] + Abi(#[from] zabi::Error), /// Failed to parse WASM with binary reader. #[error(transparent)] BinaryReader(#[from] wasmparser::BinaryReaderError), @@ -22,8 +25,8 @@ pub enum Error { #[error("Program counter {0} already exists in jump table")] DuplicateJump(u16), /// Failed to find ext function index in jump table. - #[error("External function {0:?} not found in jump table")] - ExtNotFound(crate::Func), + #[error("External function not found in jump table")] + ExtFuncNotFound, /// Failed to find function index in jump table. #[error("Function {0} not found in jump table")] FuncNotFound(u32), @@ -39,6 +42,9 @@ pub enum Error { /// Failed to mark else block for if block. #[error("Invalid else block for if block at {0}")] InvalidElseBlock(u16), + /// Failed parse function signature. + #[error("Invalid function signature")] + InvalidFunctionSignature, /// Failed to get local with given index. #[error("Invalid local index {0}")] InvalidLocalIndex(usize), @@ -72,6 +78,9 @@ pub enum Error { /// Failed to index data on memory. #[error("Memory index is out of range")] MemoryOutOfBounds, + /// Failed to find function selectors.0 + #[error("Function selector is not found.")] + SelectorNotFound, /// Failed to index data on stack. #[error("Stack index is out of range {0}, max is 32 (0x400)")] StackIndexOutOfRange(u8), diff --git a/codegen/src/visitor/call.rs b/codegen/src/visitor/call.rs index d48bd7d31..fea2c2083 100644 --- a/codegen/src/visitor/call.rs +++ b/codegen/src/visitor/call.rs @@ -30,11 +30,15 @@ impl CodeGen { fn call_internal(&mut self, index: u32) -> Result<()> { // record the current program counter and // pass it to the callee function. - self.masm._pc()?; + self.table.offset(self.masm.pc_offset(), 6); + self.masm.increment_sp(1)?; + self.masm._jumpdest()?; // Call an internal function. // // register the call index to the jump table. + // + // TODO: support same pc different jumps. (#160) self.table.call(self.masm.pc_offset(), index); // jump to the callee function @@ -56,18 +60,6 @@ impl CodeGen { .get(&index) .ok_or(Error::ImportedFuncNotFound(index))?; - func.prelude(&mut self.masm)?; - if func.is_embedded() { - tracing::debug!("embed imported function {func:?} at index {index}"); - self.call_embedded(func) - } else { - tracing::debug!("call imported function {func:?} at index {index}"); - self.call_external(func) - } - } - - /// Call embedded functions - fn call_embedded(&mut self, func: Func) -> Result<()> { match func { Func::Sstore => self.masm._sstore(), Func::Sload => self.masm._sload(), @@ -82,19 +74,4 @@ impl CodeGen { } } } - - /// Call external functions - pub fn call_external(&mut self, func: Func) -> Result<()> { - // record the current program counter and - // pass it to the callee function. - self.masm._pc()?; - - // register function to the jump table. - self.table.ext(self.masm.pc_offset(), func); - self.masm.decrement_sp(func.stack_in())?; - self.masm._jump()?; - self.masm._jumpdest()?; - self.masm.increment_sp(func.stack_out())?; - Ok(()) - } } diff --git a/codegen/src/visitor/control.rs b/codegen/src/visitor/control.rs index ee2fe58db..a71ea8dd1 100644 --- a/codegen/src/visitor/control.rs +++ b/codegen/src/visitor/control.rs @@ -1,8 +1,9 @@ //! Control flow visitors use crate::{ + code::ExtFunc, control::{ControlStackFrame, ControlStackFrameType}, - CodeGen, Func, Result, + CodeGen, Result, }; use wasmparser::{BlockType, BrTable}; @@ -90,16 +91,20 @@ impl CodeGen { /// STACK: [val1, val2, cond] -> \[val1\] if cond is non-zero, \[val2\] otherwise. pub fn _select(&mut self) -> Result<()> { tracing::trace!("select"); - let func = Func::Select; - func.prelude(&mut self.masm)?; - self.masm.decrement_sp(func.stack_in())?; + let ext = ExtFunc::select(); - // This if for pushing the PC of jumpdest. - self.masm.increment_sp(1)?; - self.table.ext(self.masm.pc_offset(), Func::Select); + // Reorder the stack inputs. + self.masm._pc()?; + self.masm._swap2()?; + self.masm._swap1()?; + + // Decrement the stack pointer. + self.masm.decrement_sp(ext.stack_in)?; + + self.table.ext(self.masm.pc_offset(), ext.clone()); self.masm._jumpi()?; self.masm._jumpdest()?; - self.masm.increment_sp(func.stack_out())?; + self.masm.increment_sp(ext.stack_out)?; Ok(()) } diff --git a/codegen/src/visitor/handlers.rs b/codegen/src/visitor/handlers.rs index 060f09397..41f64b682 100644 --- a/codegen/src/visitor/handlers.rs +++ b/codegen/src/visitor/handlers.rs @@ -1,34 +1,14 @@ //! Case handlers -use crate::{CodeGen, ControlStackFrame, ControlStackFrameType, Error, Result, ToLSBytes}; +use crate::{CodeGen, ControlStackFrame, ControlStackFrameType, Result}; impl CodeGen { - pub(crate) fn handle_empty_return(&mut self) -> Result<()> { - self.masm._push0()?; - self.masm._push0()?; - self.masm.asm._return()?; - - Ok(()) - } - /// Handle the end of the function. pub(crate) fn handle_return(&mut self) -> Result<()> { let results = self.env.results(); tracing::debug!("handle return, results: {results:?}"); - if results.is_empty() { - return self.handle_empty_return(); - } - - let size = self.masm.memory_write(results)?.size; - let offset = self - .masm - .mp_offset(|mp| mp.checked_sub(size).ok_or_else(|| Error::InvalidMP(0)))?; - - self.masm.push(&size.to_ls_bytes())?; - self.masm.push(&offset)?; - self.masm.asm._return()?; - Ok(()) + self.masm.main_return(results) } /// Handle the return of a call. @@ -36,27 +16,7 @@ impl CodeGen { let results = self.env.results(); tracing::debug!("handle call return: {:?}", results); - let len = results.len() as u8; - let sp = self.masm.sp(); - for i in 0..len { - // TODO: arthmetic overflow. - // - // 2 is for PC & self. - self.masm.swap(sp - i - 2)?; - } - - tracing::debug!("cleaning frame stack, target: {}", len + 1); - while self.masm.sp() > len + 1 { - self.masm._drop()?; - } - - // TODO: handle the length of results > u8::MAX. - self.masm.shift_pc(len, false)?; - self.masm.push(&[0x04])?; - self.masm._add()?; - self.masm._jump()?; - - Ok(()) + self.masm.call_return(results) } /// Handle the popping of a frame. diff --git a/codegen/src/visitor/local.rs b/codegen/src/visitor/local.rs index d5f34cd2f..5acb3ea9f 100644 --- a/codegen/src/visitor/local.rs +++ b/codegen/src/visitor/local.rs @@ -65,6 +65,7 @@ impl CodeGen { let sp = self.masm.sp(); tracing::debug!("local_get: {local_index} {local_sp} {sp}"); + // TODO: Arthmetic checks self.masm.dup(sp - local_sp)?; Ok(()) diff --git a/codegen/src/visitor/log.rs b/codegen/src/visitor/log.rs index ae4b77892..b1b712d1e 100644 --- a/codegen/src/visitor/log.rs +++ b/codegen/src/visitor/log.rs @@ -8,7 +8,7 @@ impl CodeGen { let buffer: Vec = self.masm.buffer().into(); // Pop offset and size from the bytecode. - let data_len = self.backtrace.popn(2); + let data_len = self.backtrace.popn(2).concat().len(); let data = &buffer[(buffer.len() - data_len)..]; *self.masm.buffer_mut() = buffer[..(buffer.len() - data_len)].into(); diff --git a/codegen/src/visitor/mod.rs b/codegen/src/visitor/mod.rs index f8956ba65..8485a91fe 100644 --- a/codegen/src/visitor/mod.rs +++ b/codegen/src/visitor/mod.rs @@ -46,8 +46,8 @@ macro_rules! map_wasm_operators { let before = self.masm.buffer().len(); self.masm.[< _ $evm >]()?; - let after = self.masm.buffer().len(); - self.backtrace.push(after - before); + let instr = self.masm.buffer()[before..].to_vec(); + self.backtrace.push(instr); Ok(()) } @@ -100,8 +100,9 @@ macro_rules! map_wasm_operators { let before = self.masm.buffer().len(); self.$($field.)*[< _ $evm >]($($arg),*)?; - let after = self.masm.buffer().len(); - self.backtrace.push(after - before); + + let instr = self.masm.buffer()[before..].to_vec(); + self.backtrace.push(instr); Ok(()) } } diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index 19023b9e5..ba08ff657 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -1,9 +1,9 @@ //! Zink compiler use crate::{parser::Parser, Error, Result}; -use wasmparser::{FuncValidator, FunctionBody, ValidatorResources, WasmModuleResources}; use zingen::{ - Buffer, CodeGen, DataSet, Dispatcher, Exports, Functions, Imports, JumpTable, BUFFER_LIMIT, + Buffer, CodeGen, DataSet, Dispatcher, Exports, Function, Functions, Imports, JumpTable, + BUFFER_LIMIT, }; /// Zink Compiler @@ -11,9 +11,16 @@ use zingen::{ pub struct Compiler { buffer: Buffer, table: JumpTable, + dispatcher: bool, } impl Compiler { + /// Embed dispatcher in bytecode. + pub fn with_dispatcher(&mut self) -> &mut Self { + self.dispatcher = true; + self + } + /// Compile wasm module to evm bytecode. pub fn compile(mut self, wasm: &[u8]) -> Result { let Parser { @@ -27,10 +34,12 @@ impl Compiler { tracing::trace!("exports: {:?}", exports); let selectors = funcs.drain_selectors(&exports); - self.compile_dispatcher(data.clone(), exports, imports.clone(), selectors)?; + if !selectors.is_empty() && self.dispatcher { + self.compile_dispatcher(data.clone(), exports, &funcs, imports.clone(), selectors)?; + } for func in funcs.into_funcs() { - self.compile_func(data.clone(), imports.clone(), func.validator, func.body)?; + self.compile_func(data.clone(), imports.clone(), func)?; } self.finish() @@ -41,10 +50,11 @@ impl Compiler { &mut self, data: DataSet, exports: Exports, + funcs: &Functions, imports: Imports, selectors: Functions, ) -> Result<()> { - let mut dispatcher = Dispatcher::default(); + let mut dispatcher = Dispatcher::new(funcs); dispatcher.data(data).exports(exports).imports(imports); let buffer = dispatcher.finish(selectors, &mut self.table)?; @@ -62,25 +72,25 @@ impl Compiler { &mut self, dataset: DataSet, imports: Imports, - mut validator: FuncValidator, - body: FunctionBody, + mut func: Function<'_>, ) -> Result<()> { - let func_index = validator.index(); - let sig = validator - .resources() - .type_of_function(func_index) - .ok_or(Error::InvalidFunctionSignature)? - .clone(); + let func_index = func.index(); + let sig = func.sig()?; tracing::debug!("compile function {}: {:?}", func_index, sig); - let is_main = func_index - (imports.len() as u32) == 0; + let is_main = if self.dispatcher { + false + } else { + func_index - (imports.len() as u32) == 0 + }; + let mut codegen = CodeGen::new(sig, dataset, imports, is_main)?; - let mut locals_reader = body.get_locals_reader()?; - let mut ops_reader = body.get_operators_reader()?; + let mut locals_reader = func.body.get_locals_reader()?; + let mut ops_reader = func.body.get_operators_reader()?; - codegen.emit_locals(&mut locals_reader, &mut validator)?; - codegen.emit_operators(&mut ops_reader, &mut validator)?; + codegen.emit_locals(&mut locals_reader, &mut func.validator)?; + codegen.emit_operators(&mut ops_reader, &mut func.validator)?; self.emit_buffer(func_index, codegen)?; Ok(()) diff --git a/compiler/src/result.rs b/compiler/src/result.rs index 9d875f2fe..ce632d3c7 100644 --- a/compiler/src/result.rs +++ b/compiler/src/result.rs @@ -15,9 +15,6 @@ pub enum Error { /// Failed in code generation. #[error(transparent)] Codegen(#[from] zingen::Error), - /// Failed parse function signature. - #[error("Invalid function signature")] - InvalidFunctionSignature, /// Failed to parse WASM data with data reader. #[error("Invalid data offset")] InvalidDataOffset, diff --git a/compiler/tests/common/mod.rs b/compiler/tests/common/mod.rs index 464831c5a..c38571d74 100644 --- a/compiler/tests/common/mod.rs +++ b/compiler/tests/common/mod.rs @@ -18,8 +18,8 @@ fn setup_logger() { .ok(); } -/// Load wat as wasm binary from path. -pub fn load(instr: &str, name: &str) -> Result> { +/// Setup test environment. +fn compile(compiler: Compiler, instr: &str, name: &str) -> Result> { setup_logger(); let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(format!("wat/{instr}/{name}.wat")); @@ -27,7 +27,20 @@ pub fn load(instr: &str, name: &str) -> Result> { let wat = fs::read(path)?; let wasm = wat::parse_bytes(&wat)?; - let bytecode = Compiler::default().compile(&wasm)?.to_vec(); + let bytecode = compiler.compile(&wasm)?.to_vec(); tracing::trace!("bytecode: {:?}", hex::encode(&bytecode)); Ok(bytecode) } + +/// Load wat as wasm binary from path. +pub fn load(instr: &str, name: &str) -> Result> { + compile(Compiler::default(), instr, name) +} + +#[allow(unused)] +/// Load wat as wasm binary from path with dispatcher enabled. +pub fn load_with_dispatcher(instr: &str, name: &str) -> Result> { + let mut compiler = Compiler::default(); + compiler.with_dispatcher(); + compile(compiler, instr, name) +} diff --git a/compiler/tests/storage.rs b/compiler/tests/storage.rs index 4b832f4a5..f73190ceb 100644 --- a/compiler/tests/storage.rs +++ b/compiler/tests/storage.rs @@ -45,19 +45,19 @@ fn basic() -> Result<()> { #[test] fn dispatcher() -> Result<()> { - let _bytecode = common::load("storage", "dispatcher")?; + let bytecode = common::load_with_dispatcher("storage", "dispatcher")?; // TODO: testing set (#122) - // { - // let key = 0; - // let value = 42; - // let mut selector = zabi::selector(b"set(i32)").to_vec(); - // selector = [selector, 42.to_bytes32().to_vec()].concat(); - // let info = EVM::run(&bytecode, &selector); - // assert_eq!(info.instr, InstructionResult::Return); - // assert_eq!(info.ret, []); - // assert_eq!(info.storage.get(&U256::from(key)), Some(&U256::from(value))); - // } + { + let key = 0; + let value = 42; + let mut selector = zabi::selector(b"set(i32)").to_vec(); + selector = [selector, 42.to_bytes32().to_vec()].concat(); + let info = EVM::run(&bytecode, &selector); + assert_eq!(info.instr, InstructionResult::Return); + assert_eq!(info.ret, []); + assert_eq!(info.storage.get(&U256::from(key)), Some(&U256::from(value))); + } // let info = EVM::run(&bytecode, &42.to_bytes32()); diff --git a/compiler/wat/storage/basic.wat b/compiler/wat/storage/basic.wat index 61b9c8633..d9428db01 100644 --- a/compiler/wat/storage/basic.wat +++ b/compiler/wat/storage/basic.wat @@ -4,8 +4,8 @@ (import "evm" "sstore" (func (;0;) (type 1))) (import "evm" "sload" (func (;1;) (type 0))) (func (;2;) (type 0) (param i32) (result i32) - i32.const 0 local.get 0 + i32.const 0 call 0 i32.const 0 call 1)) diff --git a/compiler/wat/storage/dispatcher.wat b/compiler/wat/storage/dispatcher.wat index d10c24ab7..b5c0ce792 100644 --- a/compiler/wat/storage/dispatcher.wat +++ b/compiler/wat/storage/dispatcher.wat @@ -9,32 +9,32 @@ (import "zinkc" "emit_abi" (func (;2;) (type 1))) (import "env" "memory" (memory (;0;) 17)) (func (;3;) (type 2) (param i32) (result i32) - i32.const 0 local.get 0 + i32.const 0 call 0 i32.const 0 call 1) (func (;4;) (type 0) i32.const 1048576 - i32.const 16 + i32.const 34 call 2) (func (;5;) (type 3) (param i32) - i32.const 0 local.get 0 + i32.const 0 call 0) (func (;6;) (type 0) - i32.const 1048592 - i32.const 8 + i32.const 1048610 + i32.const 18 call 2) (func (;7;) (type 4) (result i32) i32.const 0 call 1) (func (;8;) (type 0) - i32.const 1048600 - i32.const 5 + i32.const 1048628 + i32.const 10 call 2) - (global (;0;) i32 (i32.const 1048605)) - (global (;1;) i32 (i32.const 1048608)) + (global (;0;) i32 (i32.const 1048638)) + (global (;1;) i32 (i32.const 1048640)) (export "set_and_get" (func 3)) (export "set_and_get_selector" (func 4)) (export "set" (func 5)) @@ -43,4 +43,4 @@ (export "get_selector" (func 8)) (export "__data_end" (global 0)) (export "__heap_base" (global 1)) - (data (;0;) (i32.const 1048576) "set_and_get(i32)set(i32)get()")) + (data (;0;) (i32.const 1048576) "0b7365745f616e645f67657401036933320373657401036933320367657400")) diff --git a/compiler/wat/storage/load.wat b/compiler/wat/storage/load.wat index 88ca518e2..efcbafe07 100644 --- a/compiler/wat/storage/load.wat +++ b/compiler/wat/storage/load.wat @@ -4,8 +4,8 @@ (import "evm" "sload" (func (;0;) (type 0))) (import "evm" "sstore" (func (;1;) (type 1))) (func (type 0) (param i32) (result i32) - i32.const 0 local.get 0 + i32.const 0 call 1 i32.const 0 call 0)) diff --git a/compiler/wat/storage/store.wat b/compiler/wat/storage/store.wat index b263c01bd..c17e935c1 100644 --- a/compiler/wat/storage/store.wat +++ b/compiler/wat/storage/store.wat @@ -2,7 +2,7 @@ (type (;0;) (func (param i32 i32))) (import "evm" "sstore" (func (;0;) (type 0))) (func (param i32) - i32.const 0 local.get 0 + i32.const 0 call 0) ) diff --git a/examples/Cargo.lock b/examples/Cargo.lock index 327595cc1..0fc9552e5 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -9,18 +9,6 @@ dependencies = [ "zink", ] -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "block-buffer" version = "0.10.4" @@ -31,16 +19,16 @@ dependencies = [ ] [[package]] -name = "cfg-if" -version = "1.0.0" +name = "cobs" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" dependencies = [ "libc", ] @@ -65,6 +53,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + [[package]] name = "fibonacci" version = "0.0.0" @@ -110,16 +104,6 @@ version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.1.0" @@ -128,32 +112,14 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" +name = "postcard" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", + "cobs", + "embedded-io", + "serde", ] [[package]] @@ -175,19 +141,24 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.3.5" +name = "serde" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ - "bitflags", + "serde_derive", ] [[package]] -name = "scopeguard" -version = "1.2.0" +name = "serde_derive" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "sha3" @@ -199,12 +170,6 @@ dependencies = [ "keccak", ] -[[package]] -name = "smallvec" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" - [[package]] name = "storage" version = "0.1.0" @@ -223,6 +188,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "typenum" version = "1.17.0" @@ -242,62 +227,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +name = "zabi" +version = "0.1.4" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "hex", + "postcard", + "serde", + "sha3", + "thiserror", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "zink" version = "0.1.4" @@ -309,11 +248,8 @@ dependencies = [ name = "zink-codegen" version = "0.1.4" dependencies = [ - "hex", - "once_cell", - "parking_lot", "proc-macro2", "quote", - "sha3", "syn", + "zabi", ] diff --git a/zink/codegen/Cargo.toml b/zink/codegen/Cargo.toml index 0cbd71d67..9a8bef0e9 100644 --- a/zink/codegen/Cargo.toml +++ b/zink/codegen/Cargo.toml @@ -13,10 +13,7 @@ repository.workspace = true proc-macro = true [dependencies] -hex.workspace = true -once_cell.workspace = true -parking_lot.workspace = true proc-macro2.workspace = true quote.workspace = true syn.workspace = true -sha3.workspace = true +zabi.workspace = true diff --git a/zink/codegen/src/selector.rs b/zink/codegen/src/selector.rs index a05558aae..70754e5c8 100644 --- a/zink/codegen/src/selector.rs +++ b/zink/codegen/src/selector.rs @@ -1,10 +1,10 @@ //! Macro for the function selector. -// use crate::utils::keccak; use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; use quote::{quote, ToTokens}; use syn::{parse_quote, FnArg, ItemFn, Signature}; +use zabi::Abi; /// Mark the function as external. /// @@ -51,7 +51,10 @@ fn parse_selector(sig: &Signature) -> String { ), }); - let mut input = sig.ident.to_string(); - input = input + "(" + &args.collect::>().join(", ") + ")"; - input + Abi { + name: sig.ident.to_string(), + inputs: args.collect(), + } + .to_hex() + .expect("Failed to serialize ABI") } diff --git a/zink/codegen/src/storage.rs b/zink/codegen/src/storage.rs index e2b1a204c..17629612b 100644 --- a/zink/codegen/src/storage.rs +++ b/zink/codegen/src/storage.rs @@ -45,7 +45,7 @@ pub fn parse(input: ItemType) -> TokenStream { fn set(value: #variable_type) { unsafe { - zink::ffi::evm::sstore(Self::STORAGE_KEY, value); + zink::ffi::evm::sstore(value, Self::STORAGE_KEY); } } } @@ -61,6 +61,6 @@ mod tests { #[test] fn parse_test() { let expr: ItemType = syn::parse_str("pub type Counter = i32;").unwrap(); - assert_eq!(parse(expr).to_string().as_str(), "struct Counter ; impl zink :: Storage < i32 > for Counter { const STORAGE_KEY : i32 = 0i32 ; fn get () -> i32 { unsafe { zink :: ffi :: evm :: sload (Self :: STORAGE_KEY) } } fn set (value : i32) { unsafe { zink :: ffi :: evm :: sstore (Self :: STORAGE_KEY , value) ; } } }"); + assert_eq!(parse(expr).to_string().as_str(), "struct Counter ; impl zink :: Storage < i32 > for Counter { const STORAGE_KEY : i32 = 0i32 ; fn get () -> i32 { unsafe { zink :: ffi :: evm :: sload (Self :: STORAGE_KEY) } } fn set (value : i32) { unsafe { zink :: ffi :: evm :: sstore (value , Self :: STORAGE_KEY) ; } } }"); } } diff --git a/zink/src/ffi/evm.rs b/zink/src/ffi/evm.rs index fadd7582e..09af6e9ec 100644 --- a/zink/src/ffi/evm.rs +++ b/zink/src/ffi/evm.rs @@ -9,7 +9,7 @@ extern "C" { // i32 -> 8 bytes /// Store a value in the storage - pub fn sstore(key: i32, value: i32); + pub fn sstore(value: i32, key: i32); /// Load a value from the storage pub fn sload(key: i32) -> i32;