Skip to content

Commit

Permalink
feat(codegen): dispatching functions with function dispatcher (#153)
Browse files Browse the repository at this point in the history
* feat(compiler): introduce flag dispatcher for compiler

* feat(zabi): serde ABI

* chore(zabi): not using default features of postcard

* refactor(codegen): register compiled bytecode in backtrace

* feat(codegen): introduce offset in jump table

* feat(codegen): move return handlers to macro assembler

* refactor(codegen): introduce external function in code section

* feat(codegen): embed functions in codegen

* feat(codegen): clean instructions of the dispatcher

* feat(codegen): load calldata for dispatcher
  • Loading branch information
clearloop authored Oct 24, 2023
1 parent 6877875 commit 8dbf15f
Show file tree
Hide file tree
Showing 42 changed files with 623 additions and 622 deletions.
86 changes: 31 additions & 55 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions codegen/abi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
54 changes: 54 additions & 0 deletions codegen/abi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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<String>,
}

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<Self> {
postcard::from_bytes(bytes).map_err(Into::into)
}

/// Decode ABI form hex string.
pub fn from_hex(hex: &str) -> Result<Self> {
Self::from_bytes(&hex::decode(hex)?)
}

/// Decode ABI form hex string.
pub fn from_hex_bytes(bytes: &[u8]) -> Result<Self> {
Self::from_hex(&String::from_utf8_lossy(bytes))
}

/// Convert ABI to hex string.
pub fn to_hex(&self) -> Result<String> {
self.to_bytes().map(hex::encode)
}

/// Convert ABI to bytes.
pub fn to_bytes(&self) -> Result<Vec<u8>> {
postcard::to_stdvec(&self).map_err(Into::into)
}
}
15 changes: 15 additions & 0 deletions codegen/abi/src/result.rs
Original file line number Diff line number Diff line change
@@ -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<T> = std::result::Result<T, Error>;
1 change: 1 addition & 0 deletions codegen/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ macro_rules! offset {

offset! {
(usize, 8),
(u64, 8),
(i64, 8),
(i32, 4),
(u32, 4),
Expand Down
4 changes: 2 additions & 2 deletions codegen/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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());
Expand Down
30 changes: 17 additions & 13 deletions codegen/src/backtrace.rs
Original file line number Diff line number Diff line change
@@ -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<usize>,
/// Compiled instructions.
///
/// TODO: Transform this into Opcodes. (#21)
instrs: BTreeMap<usize, Vec<u8>>,
}

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<u8> {
self.instrs.pop_last().unwrap_or_default().1
}

pub fn popn(&mut self, n: usize) -> usize {
let mut r: Vec<usize> = Default::default();
/// Pop the last `n` operands from the backtrace.
pub fn popn(&mut self, n: usize) -> Vec<Vec<u8>> {
let mut r: Vec<Vec<u8>> = Default::default();

while r.len() < n {
r.push(self.pop())
}

r.into_iter().sum()
r
}
}
45 changes: 45 additions & 0 deletions codegen/src/code/func.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//! External Function for the code section.
use opcodes::ShangHai as OpCode;

trait OpCodesToBytes {
fn to_bytes(self) -> Vec<u8>;
}

impl OpCodesToBytes for &[OpCode] {
fn to_bytes(self) -> Vec<u8> {
[&[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<u8>,
}

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(),
}
}
}
Loading

0 comments on commit 8dbf15f

Please sign in to comment.