Skip to content

Commit

Permalink
Merge pull request #7 from allbridge-io/eof/optimization
Browse files Browse the repository at this point in the history
EOF optimization
  • Loading branch information
YaroslavNekryach authored Aug 25, 2023
2 parents 2a4305c + 0ddec77 commit b4668c2
Show file tree
Hide file tree
Showing 8 changed files with 841 additions and 1,102 deletions.
2 changes: 1 addition & 1 deletion evm_loader/program/src/account_storage/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use solana_program::clock::Clock;
use solana_program::program_error::ProgramError;
use solana_program::pubkey::Pubkey;
use solana_program::system_program;
use solana_program::sysvar::Sysvar;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use solana_program::sysvar::Sysvar;

impl<'a> ProgramAccountStorage<'a> {
pub fn new(
Expand Down
108 changes: 40 additions & 68 deletions evm_loader/program/src/evm/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,19 @@

use crate::evm::Buffer;

use super::opcode_table::OpCode;
#[allow(clippy::wildcard_imports)]
use crate::evm::opcode_table::opcode::*;

pub struct Bitvec(Vec<u8>);

const SET2BITS_MASK: u16 = 0b11;
const SET3BITS_MASK: u16 = 0b111;
const SET4BITS_MASK: u16 = 0b1111;
const SET5BITS_MASK: u16 = 0b1_1111;
const SET6BITS_MASK: u16 = 0b11_1111;
const SET7BITS_MASK: u16 = 0b111_1111;
const BITS_MASK: [u16; 8] = [0, 1, 0b11, 0b111, 0b1111, 0b1_1111, 0b11_1111, 0b111_1111];

impl Bitvec {
pub fn new(capacity: usize) -> Self {
Bitvec(vec![0; capacity])
}

pub fn set1(&mut self, pos: usize) {
pub fn _set1(&mut self, pos: usize) {
self.0[pos / 8] |= 1 << (pos % 8);
}

Expand Down Expand Up @@ -72,33 +68,36 @@ impl Bitvec {
let mut numbits: u8;
pc += 1;

if op >= OpCode::PUSH1.u8() && op <= OpCode::PUSH32.u8() {
numbits = op - OpCode::PUSH1.u8() + 1;
} else if op == OpCode::RJUMP.u8()
|| op == OpCode::RJUMPI.u8()
|| op == OpCode::CALLF.u8()
{
numbits = 2;
} else if op == OpCode::RJUMPV.u8() {
// RJUMPV is unique as it has a variable sized operand.
// The total size is determined by the count byte which
// immediate proceeds RJUMPV. Truncation will be caught
// in other validation steps -- for now, just return a
// valid bitmap for as much of the code as is
// available.
let end = code.len();
if pc >= end {
// Count missing, no more bits to mark.
return;
match op {
PUSH1..=PUSH32 => {
numbits = op - PUSH1 + 1;
}
numbits = code.get_or_default(pc) * 2 + 1;
if pc + numbits as usize > end {
// Jump table is truncated, mark as many bits
// as possible.
numbits = (end - pc) as u8; // TODO: check overflow

RJUMP | RJUMPI | CALLF => {
numbits = 2;
}

RJUMPV => {
// RJUMPV is unique as it has a variable sized operand.
// The total size is determined by the count byte which
// immediate proceeds RJUMPV. Truncation will be caught
// in other validation steps -- for now, just return a
// valid bitmap for as much of the code as is
// available.
let end = code.len();
if pc >= end {
// Count missing, no more bits to mark.
return;
}
numbits = code.get_or_default(pc) * 2 + 1;
if pc + numbits as usize > end {
// Jump table is truncated, mark as many bits
// as possible.
numbits = (end - pc) as u8;
}
}
} else {
continue;

_ => continue,
}

if numbits >= 8 {
Expand All @@ -114,37 +113,10 @@ impl Bitvec {
}
}

match numbits {
1 => {
self.set1(pc);
pc += 1;
}
2 => {
self.set_n(SET2BITS_MASK, pc);
pc += 2;
}
3 => {
self.set_n(SET3BITS_MASK, pc);
pc += 3;
}
4 => {
self.set_n(SET4BITS_MASK, pc);
pc += 4;
}
5 => {
self.set_n(SET5BITS_MASK, pc);
pc += 5;
}
6 => {
self.set_n(SET6BITS_MASK, pc);
pc += 6;
}
7 => {
self.set_n(SET7BITS_MASK, pc);
pc += 7;
}
_ => (),
};
if (1..=7).contains(&numbits) {
self.set_n(BITS_MASK[numbits as usize], pc);
pc += numbits as usize;
}
}
}
}
Expand All @@ -153,26 +125,26 @@ impl Bitvec {
#[cfg(test)]
mod tests {
use crate::evm::analysis::Bitvec;
use crate::evm::opcode_table::OpCode::*;
use crate::evm::opcode_table::opcode::*;
use crate::evm::Buffer;

#[test]
fn eof_code_bitmap_test1() {
let code = Buffer::from_slice(&[RJUMP.u8(), 0x01, 0x01, 0x01]);
let code = Buffer::from_slice(&[RJUMP, 0x01, 0x01, 0x01]);
let bitvec = Bitvec::eof_code_bitmap(&code);
assert_eq!(bitvec.to_vec()[0], 0b0000_0110);
}

#[test]
fn eof_code_bitmap_test2() {
let code = Buffer::from_slice(&[RJUMPI.u8(), RJUMP.u8(), RJUMP.u8(), RJUMPI.u8()]);
let code = Buffer::from_slice(&[RJUMPI, RJUMP, RJUMP, RJUMPI]);
let bitvec = Bitvec::eof_code_bitmap(&code);
assert_eq!(bitvec.to_vec()[0], 0b0011_0110);
}

#[test]
fn eof_code_bitmap_test3() {
let code = Buffer::from_slice(&[RJUMPV.u8(), 0x02, RJUMP.u8(), 0x00, RJUMPI.u8(), 0x00]);
let code = Buffer::from_slice(&[RJUMPV, 0x02, RJUMP, 0x00, RJUMPI, 0x00]);
let bitvec = Bitvec::eof_code_bitmap(&code);
assert_eq!(bitvec.to_vec()[0], 0b0011_1110);
}
Expand Down
4 changes: 2 additions & 2 deletions evm_loader/program/src/evm/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl Buffer {
#[inline]
#[must_use]
pub fn get_u16_or_default(&self, index: usize) -> u16 {
if self.len() < 2 {
if self.len() < index + 2 {
return u16::default();
};

Expand All @@ -138,7 +138,7 @@ impl Buffer {
#[inline]
#[must_use]
pub fn get_i16_or_default(&self, index: usize) -> i16 {
if self.len() < 2 {
if self.len() < index + 2 {
return i16::default();
};

Expand Down
2 changes: 1 addition & 1 deletion evm_loader/program/src/evm/eof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ impl Container {

bytes.extend(type_section_content);

bytes.extend(self.code.clone().iter().flat_map(|buf| buf.to_vec()));
bytes.extend(self.code.iter().flat_map(|buf| buf.to_vec()));
bytes.extend(self.data.to_vec());

Buffer::from_slice(&bytes)
Expand Down
47 changes: 25 additions & 22 deletions evm_loader/program/src/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ impl<B: Database> Machine<B> {
reinit_buffer(&mut machine.execution_code, backend);
reinit_buffer(&mut machine.return_data, backend);

if let Some(container) = &mut machine.container {
for code in &mut container.code {
reinit_buffer(code, backend);
}
reinit_buffer(&mut container.data, backend);
}

if let Some(parent) = &mut machine.parent {
reinit_machine(parent, backend);
}
Expand Down Expand Up @@ -310,11 +317,8 @@ impl<B: Database> Machine<B> {
}

pub fn execute(&mut self, step_limit: u64, backend: &mut B) -> Result<(ExitStatus, u64)> {
let mut code = match &self.container {
Some(container) => &container.code[self.code_section],
None => &self.execution_code,
}
.clone();
let code = self.get_code();

assert!(code.uninit_data().is_none());
assert!(self.call_data.uninit_data().is_none());
assert!(self.return_data.uninit_data().is_none());
Expand All @@ -323,9 +327,15 @@ impl<B: Database> Machine<B> {

tracing_event!(tracing::Event::BeginVM {
context: self.context,
code: code.clone().to_vec()
code: self.execution_code.to_vec()
});

let opcode_table = if self.container.is_some() {
Self::EOF_OPCODES
} else {
Self::OPCODES
};

let status = loop {
if is_precompile_address(&self.context.contract) {
let value = precompile(&self.context.contract, &self.call_data).unwrap_or_default();
Expand All @@ -340,6 +350,7 @@ impl<B: Database> Machine<B> {
break ExitStatus::StepLimit;
}

let code = self.get_code();
let opcode = code.get_or_default(self.pc);

tracing_event!(tracing::Event::BeginStep {
Expand All @@ -349,12 +360,6 @@ impl<B: Database> Machine<B> {
memory: self.memory.to_vec()
});

let opcode_table = if self.container.is_some() {
Self::EOF_OPCODES
} else {
Self::OPCODES
};

// SAFETY: OPCODES.len() == 256, opcode <= 255
let opcode_fn = unsafe { opcode_table.get_unchecked(opcode as usize) };

Expand All @@ -379,7 +384,6 @@ impl<B: Database> Machine<B> {
Action::Revert(value) => break ExitStatus::Revert(value),
Action::CodeSection(code_section, pc) => {
self.code_section = code_section;
code = self.get_code().clone();
self.pc = pc;
}
Action::Suicide => break ExitStatus::Suicide,
Expand All @@ -396,13 +400,8 @@ impl<B: Database> Machine<B> {

#[must_use]
pub fn get_code(&self) -> &Buffer {
self.code_at(self.code_section)
}

#[must_use]
pub fn code_at(&self, code_section: usize) -> &Buffer {
match &self.container {
Some(container) => &container.code[code_section],
Some(container) => &container.code[self.code_section],
None => &self.execution_code,
}
}
Expand All @@ -416,7 +415,11 @@ impl<B: Database> Machine<B> {
gas_limit: Option<U256>,
) -> Result<()> {
let container = if has_eof_magic(&execution_code) {
Some(Container::unmarshal_binary(&execution_code)?)
let container = Container::unmarshal_binary(&execution_code)?;
if reason == Reason::Create {
container.validate_container()?;
}
Some(container)
} else {
None
};
Expand All @@ -434,12 +437,12 @@ impl<B: Database> Machine<B> {
stack: Stack::new(),
memory: Memory::new(),
pc: 0_usize,
code_section: 0, // TODO: set if need
code_section: 0,
return_stack: vec![ReturnContext {
stack_height: 0,
pc: 0,
section: 0,
}], // TODO: set if need
}],
is_static: self.is_static,
reason,
parent: None,
Expand Down
7 changes: 1 addition & 6 deletions evm_loader/program/src/evm/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ use solana_program::log::sol_log_data;

use super::eof::has_eof_magic;
use super::{database::Database, tracing_event, Context, Machine, Reason};
use crate::evm::eof::Container;
use crate::{
error::{Error, Result},
evm::{trace_end_step, Buffer},
types::Address,
};
use crate::evm::eof::Container;

#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ReturnContext {
Expand Down Expand Up @@ -1080,11 +1080,6 @@ impl<B: Database> Machine<B> {
}

self.fork(Reason::Create, context, init_code, Buffer::empty(), None)?;

if let Some(container) = &self.container {
container.validate_container()?;
}

backend.snapshot();

sol_log_data(&[b"ENTER", b"CREATE", address.as_bytes()]);
Expand Down
Loading

0 comments on commit b4668c2

Please sign in to comment.