Skip to content

Commit

Permalink
Fix(relooper): CFG reduction algorithm improvements (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
shishkin-pavel authored Jul 31, 2023
1 parent 2f341c4 commit b917559
Show file tree
Hide file tree
Showing 40 changed files with 3,380 additions and 1,046 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = [
"bin/evm2near",
"lib/evmlib",
"lib/abi-types",
"lib/relooper"
"lib/relooper",
]

# Enable basic optimizations only, to aid debugging:
Expand Down
5 changes: 4 additions & 1 deletion bin/evm2near/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ clap = { version = "3.2.17", features = ["color", "derive", "unicode"] }
ethnum = "1.2.2"
evm_rs = "0.3.2"
hex = "0.4.3"
parity-wasm = "0.45.0"
serde = { version = "1.0.144", features = ["derive"] }
serde_json = "1.0.85"
sha3 = "0.10"
wild = "2.1.0"
wasmparser = "0.102.0"
wasm-encoder = "0.25.0"
anyhow = "1.0"
flame = "0.2.2"

[[bin]]
name = "evm2near"
Expand Down
114 changes: 51 additions & 63 deletions bin/evm2near/src/analyze.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// This is free and unencumbered software released into the public domain.

use evm_rs::{Opcode, Program};
use relooper::graph::cfg::{Cfg, CfgEdge};
use relooper::graph::{
cfg::{Cfg, CfgEdge},
GraphMut,
};

use std::{
collections::HashMap,
fmt::{Debug, Display},
Expand All @@ -10,7 +14,7 @@ use std::{

/// This struct represents offset of instruction in EVM bytecode.
/// Also look at docs of Idx struct
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Offs(pub usize);

/// This struct represents the serial number of instruction.
Expand All @@ -25,6 +29,12 @@ pub struct Offs(pub usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Idx(pub usize);

impl Debug for Offs {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Offs({})", self)
}
}

impl Display for Offs {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "0x{:x}", self.0)
Expand All @@ -43,23 +53,38 @@ pub struct NodeInfo {
pub is_dynamic: bool,
}

/// Represents either original node or artificial `Dynamic` node used for dynamic edges translation.
/// During codegen phase, all dynamic node edges will be converted to table branch.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CfgNode<T> {
Orig(T),
Dynamic,
}

impl<T: Display> Display for CfgNode<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Orig(offs) => write!(f, "{}", offs),
Self::Dynamic => write!(f, "dynamic"),
}
}
}

#[derive(Debug)]
pub struct BasicCfg {
pub cfg: Cfg<Offs>,
pub node_info: HashMap<Offs, NodeInfo>,
pub cfg: Cfg<CfgNode<Offs>>,
pub code_ranges: HashMap<Offs, Range<Idx>>,
}

pub fn basic_cfg(program: &Program) -> BasicCfg {
struct BlockStart {
start_offs: Offs,
start_idx: Idx,
is_jumpdest: bool,
}

let mut cfg = Cfg::new(Offs(0));
let mut node_info: HashMap<Offs, NodeInfo> = Default::default();
let mut cfg = Cfg::new(CfgNode::Orig(Offs(0)));
let mut code_ranges: HashMap<Offs, Range<Idx>> = Default::default();
let mut jumpdests: Vec<Offs> = Default::default();

let mut curr_offs = Offs(0);
let mut block_start: Option<BlockStart> = None;
Expand All @@ -76,7 +101,6 @@ pub fn basic_cfg(program: &Program) -> BasicCfg {
let BlockStart {
start_offs,
start_idx,
is_jumpdest,
} = block_start.expect("block should be present at any jump opcode");

let label = match prev_op {
Expand All @@ -85,77 +109,47 @@ pub fn basic_cfg(program: &Program) -> BasicCfg {
Some(_) => None,
None => unreachable!(),
};
let is_dynamic = match label {
Some(l) => {
let edge = if op == &JUMP {
CfgEdge::Uncond(l)
} else {
CfgEdge::Cond(l, next_offs)
};
cfg.add_edge(start_offs, edge);
false
}
None => true,

let jump_to = label.map(CfgNode::Orig).unwrap_or(CfgNode::Dynamic);
let edge = if op == &JUMP {
CfgEdge::Uncond(jump_to)
} else {
CfgEdge::Cond(jump_to, CfgNode::Orig(next_offs))
};
node_info.insert(
start_offs,
NodeInfo {
is_jumpdest,
is_dynamic,
},
);
cfg.add_edge(CfgNode::Orig(start_offs), edge);

code_ranges.insert(start_offs, start_idx..next_idx);
if is_jumpdest && is_dynamic {
cfg.add_node(start_offs);
}

None
}
JUMPDEST => {
jumpdests.push(curr_offs);
if let Some(BlockStart {
start_offs,
start_idx,
is_jumpdest,
}) = block_start
{
let edge = CfgEdge::Uncond(curr_offs);
cfg.add_edge(start_offs, edge);
node_info.insert(
start_offs,
NodeInfo {
is_jumpdest,
is_dynamic: false,
},
);
let edge = CfgEdge::Uncond(CfgNode::Orig(curr_offs));
cfg.add_edge(CfgNode::Orig(start_offs), edge);
code_ranges.insert(start_offs, start_idx..curr_idx);
}

Some(BlockStart {
start_offs: curr_offs,
start_idx: curr_idx,
is_jumpdest: true,
})
}
_ => {
let bs @ BlockStart {
start_offs,
start_idx,
is_jumpdest,
} = block_start.unwrap_or(BlockStart {
start_offs: curr_offs,
start_idx: curr_idx,
is_jumpdest: false,
});

if op.is_halt() {
cfg.add_edge(bs.start_offs, CfgEdge::Terminal);
node_info.insert(
start_offs,
NodeInfo {
is_jumpdest,
is_dynamic: false,
},
);
cfg.add_edge(CfgNode::Orig(bs.start_offs), CfgEdge::Terminal);
code_ranges.insert(start_offs, start_idx..next_idx);
None
} else {
Expand All @@ -171,22 +165,16 @@ pub fn basic_cfg(program: &Program) -> BasicCfg {
if let Some(BlockStart {
start_offs,
start_idx,
is_jumpdest,
}) = block_start
{
node_info.insert(
start_offs,
NodeInfo {
is_jumpdest,
is_dynamic: false,
},
);
code_ranges.insert(start_offs, start_idx..next_idx);
}

BasicCfg {
cfg,
node_info,
code_ranges,
}
let jump_table: Vec<_> = jumpdests
.into_iter()
.map(|j| (j.0, CfgNode::Orig(j)))
.collect();
cfg.add_edge(CfgNode::Dynamic, CfgEdge::Switch(jump_table));

BasicCfg { cfg, code_ranges }
}
Loading

0 comments on commit b917559

Please sign in to comment.