Skip to content

Commit

Permalink
perf(vm): general performance/space improvements (#482)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon-Becker authored Aug 25, 2024
1 parent 55b01ff commit 90e6622
Show file tree
Hide file tree
Showing 11 changed files with 73 additions and 71 deletions.
4 changes: 2 additions & 2 deletions crates/cfg/src/core/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use alloy::primitives::U256;
use eyre::{OptionExt, Result};
use heimdall_common::utils::strings::encode_hex_reduced;
use heimdall_vm::{
core::opcodes::{OpCodeInfo, JUMPDEST},
core::opcodes::{opcode_name, JUMPDEST},
ext::exec::VMTrace,
};
use petgraph::{matrix_graph::NodeIndex, Graph};
Expand All @@ -21,7 +21,7 @@ pub fn build_cfg(

// add the current operations to the cfg
for operation in &vm_trace.operations {
let opcode_name = OpCodeInfo::from(operation.last_instruction.opcode).name();
let opcode_name = opcode_name(operation.last_instruction.opcode);

let assembly = format!(
"{} {} {}",
Expand Down
10 changes: 5 additions & 5 deletions crates/decompile/src/utils/heuristics/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use alloy::primitives::U256;
use eyre::eyre;
use heimdall_common::utils::strings::find_balanced_encapsulator;
use heimdall_vm::core::{
opcodes::{OpCodeInfo, CALLDATALOAD, ISZERO},
opcodes::{opcode_name, CALLDATALOAD, ISZERO},
types::{byte_size_to_type, convert_bitmask},
vm::State,
};
Expand Down Expand Up @@ -72,7 +72,7 @@ pub fn argument_heuristic(
debug!(
"instruction {} ({}) indicates argument {} is masked to {} bytes",
state.last_instruction.instruction,
OpCodeInfo::from(state.last_instruction.opcode).name(),
opcode_name(state.last_instruction.opcode),
arg_index,
mask_size_bytes
);
Expand Down Expand Up @@ -216,7 +216,7 @@ pub fn argument_heuristic(
debug!(
"instruction {} ({}) indicates argument {} may be a numeric type",
state.last_instruction.instruction,
OpCodeInfo::from(state.last_instruction.opcode).name(),
opcode_name(state.last_instruction.opcode),
arg_index
);

Expand All @@ -239,7 +239,7 @@ pub fn argument_heuristic(
debug!(
"instruction {} ({}) indicates argument {} may be a bytes type",
state.last_instruction.instruction,
OpCodeInfo::from(state.last_instruction.opcode).name(),
opcode_name(state.last_instruction.opcode),
arg_index
);

Expand All @@ -262,7 +262,7 @@ pub fn argument_heuristic(
debug!(
"instruction {} ({}) indicates argument {} may be a boolean",
state.last_instruction.instruction,
OpCodeInfo::from(state.last_instruction.opcode).name(),
opcode_name(state.last_instruction.opcode),
arg_index
);

Expand Down
27 changes: 7 additions & 20 deletions crates/decompile/src/utils/heuristics/modifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,29 @@ use tracing::debug;

use crate::{core::analyze::AnalyzerState, interfaces::AnalyzedFunction, Error};

use lazy_static::lazy_static;

lazy_static! {
/// A list of opcodes that are considered non-pure (state accessing)
pub static ref NON_PURE_OPCODES: Vec<u8> = vec![
0x31, 0x32, 0x33, 0x3a, 0x3b, 0x3c, 0x40, 0x41, 0x42,
0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x54, 0x55, 0xf0,
0xf1, 0xf2, 0xf4, 0xf5, 0xfa, 0xff
];
/// A list of opcodes that are considered non-view (state modifying)
pub static ref NON_VIEW_OPCODES: Vec<u8> = vec![
0x55, 0xf0, 0xf1, 0xf2, 0xf4, 0xf5, 0xfa, 0xff
];
}

pub fn modifier_heuristic(
function: &mut AnalyzedFunction,
state: &State,
_: &mut AnalyzerState,
) -> Result<(), Error> {
let opcode_name = OpCodeInfo::from(state.last_instruction.opcode).name();
let opcode_info = OpCodeInfo::from(state.last_instruction.opcode);

// if any instruction is non-pure, the function is non-pure
if function.pure && NON_PURE_OPCODES.contains(&state.last_instruction.opcode) {
if function.pure && !opcode_info.is_pure() {
debug!(
"instruction {} ({}) indicates a non-pure function",
state.last_instruction.instruction, opcode_name
state.last_instruction.instruction,
opcode_info.name()
);
function.pure = false;
}

// if any instruction is non-view, the function is non-view
if function.view && NON_VIEW_OPCODES.contains(&state.last_instruction.opcode) {
if function.view && !opcode_info.is_view() {
debug!(
"instruction {} ({}) indicates a non-view function",
state.last_instruction.instruction, opcode_name
state.last_instruction.instruction,
opcode_info.name()
);
function.view = false;
}
Expand Down
4 changes: 2 additions & 2 deletions crates/decompile/src/utils/heuristics/solidity.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use alloy::primitives::U256;
use alloy_dyn_abi::{DynSolType, DynSolValue};
use heimdall_common::utils::strings::encode_hex_reduced;
use heimdall_vm::core::{opcodes::OpCodeInfo, vm::State};
use heimdall_vm::core::{opcodes::opcode_name, vm::State};

use crate::{
core::analyze::AnalyzerState,
Expand Down Expand Up @@ -188,7 +188,7 @@ pub fn solidity_heuristic(
function.logic.push(format!(
"(bool success, bytes memory ret0) = address({}).{}{}(abi.encode({}));",
address,
OpCodeInfo::from(instruction.opcode).name().to_lowercase(),
opcode_name(instruction.opcode).to_lowercase(),
modifier,
calldata
.iter()
Expand Down
6 changes: 3 additions & 3 deletions crates/decompile/src/utils/heuristics/yul.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use heimdall_common::utils::strings::encode_hex_reduced;
use heimdall_vm::core::{opcodes::OpCodeInfo, vm::State};
use heimdall_vm::core::{opcodes::opcode_name, vm::State};

use crate::{
core::analyze::AnalyzerState,
Expand All @@ -24,7 +24,7 @@ pub fn yul_heuristic(
function.memory.insert(key, StorageFrame { operation });
function.logic.push(format!(
"{}({}, {})",
OpCodeInfo::from(instruction.opcode).name().to_lowercase(),
opcode_name(instruction.opcode).to_lowercase(),
encode_hex_reduced(key),
instruction.input_operations[1].yulify()
));
Expand Down Expand Up @@ -80,7 +80,7 @@ pub fn yul_heuristic(
0xff | 0xA0 | 0xA1 | 0xA2 | 0xA3 | 0xA4 => {
function.logic.push(format!(
"{}({})",
OpCodeInfo::from(instruction.opcode).name().to_lowercase(),
opcode_name(instruction.opcode).to_lowercase(),
instruction
.input_operations
.iter()
Expand Down
4 changes: 2 additions & 2 deletions crates/disassemble/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::time::Instant;
use crate::{error::Error, interfaces::DisassemblerArgs};
use eyre::eyre;
use heimdall_common::utils::strings::encode_hex;
use heimdall_vm::core::opcodes::OpCodeInfo;
use heimdall_vm::core::opcodes::opcode_name;
use tracing::{debug, info};

pub async fn disassemble(args: DisassemblerArgs) -> Result<String, Error> {
Expand Down Expand Up @@ -45,7 +45,7 @@ pub async fn disassemble(args: DisassemblerArgs) -> Result<String, Error> {
} else {
format!("{:06x}", program_counter)
},
OpCodeInfo::from(opcode).name(),
opcode_name(opcode),
pushed_bytes
)
.as_str(),
Expand Down
6 changes: 3 additions & 3 deletions crates/vm/benches/bench_ten_thousand_hashes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use heimdall_common::utils::strings::decode_hex;
use heimdall_vm::core::vm::VM;
use tokio::runtime::Runtime;

fn test_fib(c: &mut Criterion) {
fn test_ten_thousand_hashes(c: &mut Criterion) {
let mut group = c.benchmark_group("heimdall_vm");

group.sample_size(500);
group.sample_size(100);
group.bench_function(BenchmarkId::from_parameter("ten_thousand_hashes"), |b| {
b.to_async::<Runtime>(Runtime::new().unwrap()).iter(|| async {
// build the evm
Expand All @@ -33,5 +33,5 @@ fn test_fib(c: &mut Criterion) {
group.finish();
}

criterion_group!(benches, test_fib);
criterion_group!(benches, test_ten_thousand_hashes);
criterion_main!(benches);
19 changes: 17 additions & 2 deletions crates/vm/src/core/opcodes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl OpCodeInfo {
impl From<u8> for OpCodeInfo {
#[inline]
fn from(opcode: u8) -> Self {
OPCODE_INFO_JUMPTABLE[opcode as usize].unwrap_or(OpCodeInfo {
OPCODE_INFO_TABLE[opcode as usize].unwrap_or(OpCodeInfo {
name: "unknown",
inputs: 0,
outputs: 0,
Expand Down Expand Up @@ -269,7 +269,7 @@ macro_rules! opcodes {
)*

/// Maps each opcode to its info.
pub const OPCODE_INFO_JUMPTABLE: [Option<OpCodeInfo>; 256] = {
pub const OPCODE_INFO_TABLE: [Option<OpCodeInfo>; 256] = {
let mut map = [None; 256];
let mut prev: u8 = 0;
$(
Expand All @@ -287,9 +287,24 @@ macro_rules! opcodes {
let _ = prev;
map
};

/// Maps each opcode to its name. (So we dont need to load [`OpCodeInfo`] to get the name)
pub const OPCODE_NAME_TABLE: [&'static str; 256] = {
let mut map = ["unknown"; 256];
$(
map[$val] = stringify!($name);
)*
map
};
}
}

/// Get the name of an opcode.
#[inline]
pub fn opcode_name(opcode: u8) -> &'static str {
OPCODE_NAME_TABLE[opcode as usize]
}

opcodes! {
0x00 => STOP => terminating;

Expand Down
4 changes: 2 additions & 2 deletions crates/vm/src/core/opcodes/wrapped.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use alloy::primitives::U256;

use crate::core::opcodes::OpCodeInfo;
use crate::core::opcodes::opcode_name;

/// A WrappedInput can contain either a raw U256 value or a WrappedOpcode
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -30,7 +30,7 @@ impl std::fmt::Display for WrappedOpcode {
write!(
f,
"{}({})",
OpCodeInfo::from(self.opcode).name(),
opcode_name(self.opcode),
self.inputs.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ")
)
}
Expand Down
Loading

0 comments on commit 90e6622

Please sign in to comment.