mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-14 20:21:07 +00:00
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
*.dot
|
||||||
Generated
+1483
File diff suppressed because it is too large
Load Diff
+14
@@ -0,0 +1,14 @@
|
|||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
|
members = [
|
||||||
|
"crates/ir-tac",
|
||||||
|
"crates/cli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
evmil = "0.4"
|
||||||
|
hex = "0.4"
|
||||||
|
petgraph = "0.6"
|
||||||
|
primitive-types = "0.12"
|
||||||
|
indexmap = "2.1.0"
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "revive"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
hex = { workspace = true }
|
||||||
|
evmil = { workspace = true }
|
||||||
|
ir-tac = { path = "../ir-tac" }
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
use evmil::bytecode::Disassemble;
|
||||||
|
use ir_tac::cfg::{BasicBlockFormatOption, Program};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let hexcode = std::fs::read_to_string(std::env::args().nth(1).unwrap()).unwrap();
|
||||||
|
let bytecode = hex::decode(hexcode.trim()).unwrap();
|
||||||
|
let instructions = bytecode.disassemble();
|
||||||
|
|
||||||
|
//for instruction in instructions.iter() {
|
||||||
|
// println!("{instruction:?}");
|
||||||
|
//}
|
||||||
|
|
||||||
|
Program::new(instructions).dot(BasicBlockFormatOption::ByteCode);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "ir-tac"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
evmil = { workspace = true }
|
||||||
|
petgraph = { workspace = true }
|
||||||
|
primitive-types = { workspace = true }
|
||||||
|
indexmap = { workspace = true }
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
use primitive_types::U256;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Kind {
|
||||||
|
Constant(U256),
|
||||||
|
Temporary(usize),
|
||||||
|
Stack,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Address {
|
||||||
|
pub kind: Kind,
|
||||||
|
pub type_hint: Option<Type>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(Kind, Option<Type>)> for Address {
|
||||||
|
fn from(value: (Kind, Option<Type>)) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: value.0,
|
||||||
|
type_hint: value.1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Address {
|
||||||
|
pub fn new(kind: Kind, type_hint: Option<Type>) -> Self {
|
||||||
|
Self { kind, type_hint }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Type {
|
||||||
|
Int { size: u8 },
|
||||||
|
Bytes { size: u8 },
|
||||||
|
Bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type {
|
||||||
|
pub fn int(size: u8) -> Self {
|
||||||
|
Self::Int { size }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bytes(size: u8) -> Self {
|
||||||
|
Self::Bytes { size }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Type {
|
||||||
|
fn default() -> Self {
|
||||||
|
Type::Int { size: 32 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum LinearMemory {
|
||||||
|
CallData,
|
||||||
|
Memory,
|
||||||
|
ReturnData,
|
||||||
|
}
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
use std::fmt::Write;
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use evmil::bytecode;
|
||||||
|
use petgraph::{
|
||||||
|
dot::{Config, Dot},
|
||||||
|
graph::DiGraph,
|
||||||
|
stable_graph::NodeIndex,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
instruction::{self, Instruction},
|
||||||
|
symbol::SymbolTable,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct EvmInstruction {
|
||||||
|
pub bytecode_offset: usize,
|
||||||
|
pub instruction: bytecode::Instruction,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct BasicBlock {
|
||||||
|
pub entry: Option<Entry>,
|
||||||
|
pub opcodes: Range<usize>,
|
||||||
|
pub instructions: Vec<Instruction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub enum BasicBlockFormatOption {
|
||||||
|
ByteCode,
|
||||||
|
Ir,
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BasicBlock {
|
||||||
|
fn format(&self, evm_bytecode: &[EvmInstruction], options: BasicBlockFormatOption) -> String {
|
||||||
|
let offset = evm_bytecode[self.opcodes.start].bytecode_offset;
|
||||||
|
let start = if let Some(Entry::Start) = self.entry {
|
||||||
|
"Start\n".to_string()
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
let instructions = match options {
|
||||||
|
BasicBlockFormatOption::ByteCode => evm_bytecode[self.opcodes.start..self.opcodes.end]
|
||||||
|
.iter()
|
||||||
|
.fold(String::new(), |mut acc, opcode| {
|
||||||
|
writeln!(&mut acc, "{:?}", opcode.instruction).unwrap();
|
||||||
|
acc
|
||||||
|
}),
|
||||||
|
_ => String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
format!("{start}Offset: 0x{offset:02x}\n---\n{instructions}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Entry {
|
||||||
|
Start,
|
||||||
|
Jumpdest(NodeIndex),
|
||||||
|
Else(NodeIndex),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Jump {
|
||||||
|
Direct,
|
||||||
|
Indirect,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Program {
|
||||||
|
pub evm_instructions: Vec<EvmInstruction>,
|
||||||
|
pub cfg: DiGraph<BasicBlock, Jump>,
|
||||||
|
pub symbol_table: SymbolTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program {
|
||||||
|
pub fn new(bytecode: Vec<bytecode::Instruction>) -> Self {
|
||||||
|
let mut cfg = DiGraph::new();
|
||||||
|
let mut symbol_table = SymbolTable::default();
|
||||||
|
let mut evm_instructions = Vec::with_capacity(bytecode.len());
|
||||||
|
|
||||||
|
let mut current_block = Some(BasicBlock {
|
||||||
|
entry: Some(Entry::Start),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let mut bytecode_offset = 0;
|
||||||
|
|
||||||
|
for (index, opcode) in bytecode.iter().enumerate() {
|
||||||
|
evm_instructions.push(EvmInstruction {
|
||||||
|
bytecode_offset,
|
||||||
|
instruction: opcode.clone(),
|
||||||
|
});
|
||||||
|
bytecode_offset += opcode.length();
|
||||||
|
|
||||||
|
let instructions = instruction::translate(opcode, &mut symbol_table);
|
||||||
|
|
||||||
|
use bytecode::Instruction::*;
|
||||||
|
match opcode {
|
||||||
|
JUMPDEST => {
|
||||||
|
// If we are already in a bb, conclude it
|
||||||
|
let entry = current_block.take().map(|mut node| {
|
||||||
|
node.opcodes.end = index + 1;
|
||||||
|
let entry = node.entry.clone();
|
||||||
|
let node_index = cfg.add_node(node);
|
||||||
|
|
||||||
|
// If the block had an entry, add an edge from the previous block to it
|
||||||
|
if let Some(Entry::Else(incoming)) | Some(Entry::Jumpdest(incoming)) = entry
|
||||||
|
{
|
||||||
|
cfg.add_edge(incoming, node_index, Jump::Direct);
|
||||||
|
}
|
||||||
|
node_index
|
||||||
|
});
|
||||||
|
|
||||||
|
// JUMPDEST implicitly starts a new bb
|
||||||
|
current_block = Some(BasicBlock {
|
||||||
|
entry: entry.map(Entry::Jumpdest),
|
||||||
|
opcodes: Range {
|
||||||
|
start: index + 1,
|
||||||
|
end: index + 1,
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
JUMP | STOP | RETURN | REVERT | INVALID => {
|
||||||
|
// Conclude this bb; if we are not already in a bb we must create a new one
|
||||||
|
let mut node = current_block.take().unwrap_or_else(|| BasicBlock {
|
||||||
|
opcodes: Range {
|
||||||
|
start: index,
|
||||||
|
end: index + 1,
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
node.instructions.extend(instructions);
|
||||||
|
node.opcodes.end = index + 1;
|
||||||
|
|
||||||
|
let entry = node.entry.clone();
|
||||||
|
let node_index = cfg.add_node(node);
|
||||||
|
|
||||||
|
// If the block had an entry, add an edge from the previous block to it
|
||||||
|
if let Some(Entry::Else(incoming)) | Some(Entry::Jumpdest(incoming)) = entry {
|
||||||
|
cfg.add_edge(incoming, node_index, Jump::Direct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JUMPI => {
|
||||||
|
// Conclude this bb; if we are not already in a bb we must create a new one
|
||||||
|
let mut node = current_block.take().unwrap_or_else(|| BasicBlock {
|
||||||
|
opcodes: Range {
|
||||||
|
start: index,
|
||||||
|
end: index + 1,
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
node.instructions.extend(instructions);
|
||||||
|
node.opcodes.end = index + 1;
|
||||||
|
|
||||||
|
let entry = node.entry.clone();
|
||||||
|
let node_index = cfg.add_node(node);
|
||||||
|
|
||||||
|
// If the block had an entry, add an edge from the previous block to it
|
||||||
|
if let Some(Entry::Else(incoming)) | Some(Entry::Jumpdest(incoming)) = entry {
|
||||||
|
cfg.add_edge(incoming, node_index, Jump::Direct);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JUMPI implicitly starts a new bb for the else branch
|
||||||
|
current_block = Some(BasicBlock {
|
||||||
|
entry: Some(Entry::Else(node_index)),
|
||||||
|
opcodes: Range {
|
||||||
|
start: index + 1,
|
||||||
|
end: index + 1,
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => current_block
|
||||||
|
.get_or_insert(BasicBlock {
|
||||||
|
opcodes: Range {
|
||||||
|
start: index,
|
||||||
|
end: index + 1,
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.instructions
|
||||||
|
.extend(instructions),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
evm_instructions,
|
||||||
|
cfg,
|
||||||
|
symbol_table,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dot(&self, format_options: BasicBlockFormatOption) {
|
||||||
|
let get_node_attrs = move |_, (_, node): (_, &BasicBlock)| {
|
||||||
|
format!(
|
||||||
|
"label = \"{}\"",
|
||||||
|
node.format(&self.evm_instructions, format_options)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let dot = Dot::with_attr_getters(
|
||||||
|
&self.cfg,
|
||||||
|
&[Config::EdgeNoLabel, Config::NodeNoLabel],
|
||||||
|
&|_, edge| format!("label = \"{:?}\"", edge.weight()),
|
||||||
|
&get_node_attrs,
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("{dot:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,321 @@
|
|||||||
|
use evmil::bytecode::Instruction as EvmInstruction;
|
||||||
|
use primitive_types::U256;
|
||||||
|
|
||||||
|
use crate::symbol::{Global, Symbol, SymbolTable, Type};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum Instruction {
|
||||||
|
/// `x = y op z`
|
||||||
|
BinaryAssign {
|
||||||
|
x: Symbol,
|
||||||
|
y: Symbol,
|
||||||
|
operator: Operator,
|
||||||
|
z: Symbol,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// `x = op y`
|
||||||
|
UnaryAssign {
|
||||||
|
x: Symbol,
|
||||||
|
y: Symbol,
|
||||||
|
operator: Operator,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// `branch target`
|
||||||
|
UncoditionalBranch { target: Symbol },
|
||||||
|
|
||||||
|
/// `branch target if condition`
|
||||||
|
ConditionalBranch { condition: Symbol, target: Symbol },
|
||||||
|
|
||||||
|
/// `call(label, n)`
|
||||||
|
Procedure {
|
||||||
|
symbol: Global,
|
||||||
|
parameters: Vec<Symbol>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// `x = call(label, n)`
|
||||||
|
Function {
|
||||||
|
symbol: Global,
|
||||||
|
x: Symbol,
|
||||||
|
args: Vec<Symbol>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// `x = y`
|
||||||
|
Copy { x: Symbol, y: Symbol },
|
||||||
|
|
||||||
|
/// `x[index] = y`
|
||||||
|
IndexedAssign { x: Symbol, index: Symbol, y: Symbol },
|
||||||
|
|
||||||
|
/// `x = y[index]`
|
||||||
|
IndexedCopy { x: Symbol, y: Symbol, index: Symbol },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum Operator {
|
||||||
|
Add,
|
||||||
|
Mul,
|
||||||
|
Sub,
|
||||||
|
Div,
|
||||||
|
SDiv,
|
||||||
|
Mod,
|
||||||
|
SMod,
|
||||||
|
AddMod,
|
||||||
|
MulMod,
|
||||||
|
Exp,
|
||||||
|
SignExtend,
|
||||||
|
|
||||||
|
LessThat,
|
||||||
|
GreaterThan,
|
||||||
|
SignedLessThan,
|
||||||
|
SignedGreaterThan,
|
||||||
|
Eq,
|
||||||
|
IsZero,
|
||||||
|
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
Xor,
|
||||||
|
Not,
|
||||||
|
Byte,
|
||||||
|
ShiftLeft,
|
||||||
|
ShiftRight,
|
||||||
|
ShiftArithmeticRight,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StackPop {
|
||||||
|
decrement: Instruction,
|
||||||
|
load: Instruction,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pop a value from the stack.
|
||||||
|
///
|
||||||
|
/// Returns 2 `Instruction`: Decrementing the stack pointer and the value copy.
|
||||||
|
fn stack_pop(symbol_table: &mut SymbolTable) -> StackPop {
|
||||||
|
let decrement = decrement_stack_height(symbol_table);
|
||||||
|
|
||||||
|
let load = Instruction::IndexedCopy {
|
||||||
|
x: symbol_table.temporary(None),
|
||||||
|
y: symbol_table.global(Global::Stack),
|
||||||
|
index: symbol_table.global(Global::StackHeight),
|
||||||
|
};
|
||||||
|
|
||||||
|
StackPop { decrement, load }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrease the stack height by one.
|
||||||
|
fn decrement_stack_height(symbol_table: &mut SymbolTable) -> Instruction {
|
||||||
|
Instruction::BinaryAssign {
|
||||||
|
x: symbol_table.global(Global::StackHeight),
|
||||||
|
y: symbol_table.global(Global::StackHeight),
|
||||||
|
operator: Operator::Sub,
|
||||||
|
z: symbol_table.constant(U256::one(), Some(Type::Int(4))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StackPush {
|
||||||
|
assign: Instruction,
|
||||||
|
increment: Instruction,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a `value` to the stack.
|
||||||
|
///
|
||||||
|
/// Returns 2 `Instruction`: the value assign and the stack height increase.
|
||||||
|
fn stack_push(symbol_table: &mut SymbolTable, value: Symbol) -> StackPush {
|
||||||
|
let assign = Instruction::IndexedAssign {
|
||||||
|
x: symbol_table.global(Global::Stack),
|
||||||
|
index: symbol_table.global(Global::StackHeight),
|
||||||
|
y: value,
|
||||||
|
};
|
||||||
|
let increment = increment_stack_height(symbol_table);
|
||||||
|
|
||||||
|
StackPush { assign, increment }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increment the stack height by one.
|
||||||
|
fn increment_stack_height(symbol_table: &mut SymbolTable) -> Instruction {
|
||||||
|
Instruction::BinaryAssign {
|
||||||
|
x: symbol_table.global(Global::StackHeight),
|
||||||
|
y: symbol_table.global(Global::StackHeight),
|
||||||
|
operator: Operator::Add,
|
||||||
|
z: symbol_table.constant(U256::one(), Some(Type::Int(4))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instruction {
|
||||||
|
fn target_address(&self) -> Symbol {
|
||||||
|
match self {
|
||||||
|
Instruction::Copy { x, .. } => *x,
|
||||||
|
Instruction::IndexedAssign { x, .. } => *x,
|
||||||
|
Instruction::IndexedCopy { x, .. } => *x,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lower an EVM instruction into corresponding 3AC instructions.
|
||||||
|
pub fn translate(opcode: &EvmInstruction, symbol_table: &mut SymbolTable) -> Vec<Instruction> {
|
||||||
|
use EvmInstruction::*;
|
||||||
|
match opcode {
|
||||||
|
JUMPDEST => Vec::new(),
|
||||||
|
|
||||||
|
PUSH(bytes) => {
|
||||||
|
let type_hint = Some(Type::Bytes(bytes.len()));
|
||||||
|
let value = symbol_table.constant(U256::from_big_endian(bytes), type_hint);
|
||||||
|
let push = stack_push(symbol_table, value);
|
||||||
|
|
||||||
|
vec![push.assign, push.increment]
|
||||||
|
}
|
||||||
|
|
||||||
|
POP => vec![decrement_stack_height(symbol_table)],
|
||||||
|
|
||||||
|
MSTORE => {
|
||||||
|
let offset = stack_pop(symbol_table);
|
||||||
|
let value = stack_pop(symbol_table);
|
||||||
|
|
||||||
|
let store = Instruction::IndexedAssign {
|
||||||
|
x: symbol_table.global(Global::Memory),
|
||||||
|
index: offset.load.target_address(),
|
||||||
|
y: value.load.target_address(),
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![
|
||||||
|
offset.decrement,
|
||||||
|
offset.load,
|
||||||
|
value.decrement,
|
||||||
|
value.load,
|
||||||
|
store,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
JUMP => {
|
||||||
|
let target = stack_pop(symbol_table);
|
||||||
|
|
||||||
|
let jump = Instruction::UncoditionalBranch {
|
||||||
|
target: target.load.target_address(),
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![target.decrement, target.load, jump]
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN => {
|
||||||
|
let offset = stack_pop(symbol_table);
|
||||||
|
let size = stack_pop(symbol_table);
|
||||||
|
|
||||||
|
let procedure = Instruction::Procedure {
|
||||||
|
symbol: Global::Return,
|
||||||
|
parameters: vec![offset.load.target_address(), size.load.target_address()],
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![
|
||||||
|
offset.decrement,
|
||||||
|
offset.load,
|
||||||
|
size.decrement,
|
||||||
|
size.load,
|
||||||
|
procedure,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
CALLDATACOPY => {
|
||||||
|
let destination_offset = stack_pop(symbol_table);
|
||||||
|
let offset = stack_pop(symbol_table);
|
||||||
|
let size = stack_pop(symbol_table);
|
||||||
|
|
||||||
|
let parameters = vec![
|
||||||
|
destination_offset.load.target_address(),
|
||||||
|
offset.load.target_address(),
|
||||||
|
size.load.target_address(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let procedure = Instruction::Procedure {
|
||||||
|
symbol: Global::MemoryCopy,
|
||||||
|
parameters,
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![
|
||||||
|
destination_offset.decrement,
|
||||||
|
destination_offset.load,
|
||||||
|
offset.decrement,
|
||||||
|
offset.load,
|
||||||
|
size.decrement,
|
||||||
|
size.load,
|
||||||
|
procedure,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
CALLDATALOAD => {
|
||||||
|
let index = stack_pop(symbol_table);
|
||||||
|
|
||||||
|
let value = Instruction::IndexedCopy {
|
||||||
|
x: symbol_table.temporary(None),
|
||||||
|
y: symbol_table.global(Global::CallData),
|
||||||
|
index: index.load.target_address(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let push = stack_push(symbol_table, value.target_address());
|
||||||
|
|
||||||
|
vec![
|
||||||
|
index.decrement,
|
||||||
|
index.load,
|
||||||
|
value,
|
||||||
|
push.assign,
|
||||||
|
push.increment,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
//_ => todo!("{opcode}"),
|
||||||
|
_ => Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use evmil::bytecode;
|
||||||
|
use primitive_types::U256;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
instruction::Operator,
|
||||||
|
symbol::{Global, Kind, Symbol, Type},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::Instruction;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lower_push_works() {
|
||||||
|
let mut symbol_table = Default::default();
|
||||||
|
|
||||||
|
let opcode = bytecode::Instruction::PUSH(vec![0x01]);
|
||||||
|
let result = super::translate(&opcode, &mut symbol_table);
|
||||||
|
|
||||||
|
let expected = vec![
|
||||||
|
Instruction::IndexedAssign {
|
||||||
|
x: Symbol {
|
||||||
|
kind: Kind::Label(Global::Stack),
|
||||||
|
type_hint: Type::Word,
|
||||||
|
},
|
||||||
|
index: Symbol {
|
||||||
|
kind: Kind::Label(Global::StackHeight),
|
||||||
|
type_hint: Type::Int(4),
|
||||||
|
},
|
||||||
|
y: Symbol {
|
||||||
|
kind: Kind::Constant(U256::one()),
|
||||||
|
type_hint: Type::Bytes(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Instruction::BinaryAssign {
|
||||||
|
x: Symbol {
|
||||||
|
kind: Kind::Label(Global::StackHeight),
|
||||||
|
type_hint: Type::Int(4),
|
||||||
|
},
|
||||||
|
y: Symbol {
|
||||||
|
kind: Kind::Label(Global::StackHeight),
|
||||||
|
type_hint: Type::Int(4),
|
||||||
|
},
|
||||||
|
operator: Operator::Add,
|
||||||
|
z: Symbol {
|
||||||
|
kind: Kind::Constant(U256::one()),
|
||||||
|
type_hint: Type::Int(4),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(expected, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
pub mod cfg;
|
||||||
|
pub mod instruction;
|
||||||
|
pub mod symbol;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
use indexmap::IndexSet;
|
||||||
|
use primitive_types::U256;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
|
pub enum Kind {
|
||||||
|
Constant(U256),
|
||||||
|
Temporary(usize),
|
||||||
|
Label(Global),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Kind {
|
||||||
|
pub fn from_be_bytes(bytes: &[u8]) -> Self {
|
||||||
|
Self::Constant(U256::from_big_endian(bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
|
pub struct Symbol {
|
||||||
|
pub kind: Kind,
|
||||||
|
pub type_hint: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Symbol {
|
||||||
|
fn global(symbol: Global) -> Self {
|
||||||
|
let type_hint = match symbol {
|
||||||
|
Global::StackHeight => Type::Int(4),
|
||||||
|
_ => Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::new(Kind::Label(symbol), type_hint)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(kind: Kind, type_hint: Type) -> Self {
|
||||||
|
Self { kind, type_hint }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Default, Clone, Copy)]
|
||||||
|
pub enum Type {
|
||||||
|
#[default]
|
||||||
|
Word,
|
||||||
|
Int(usize),
|
||||||
|
Bytes(usize),
|
||||||
|
Bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type {
|
||||||
|
pub fn pointer() -> Self {
|
||||||
|
Self::Int(4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
|
pub enum Global {
|
||||||
|
/// Pointer
|
||||||
|
Stack,
|
||||||
|
/// Stack height variable
|
||||||
|
StackHeight,
|
||||||
|
|
||||||
|
/// Pointer
|
||||||
|
CallData,
|
||||||
|
/// Pointer
|
||||||
|
Memory,
|
||||||
|
/// Pointer
|
||||||
|
ReturnData,
|
||||||
|
|
||||||
|
/// Low level `memcpy` like function
|
||||||
|
MemoryCopy,
|
||||||
|
|
||||||
|
// EVM
|
||||||
|
Sha3,
|
||||||
|
Address,
|
||||||
|
CallDataLoad,
|
||||||
|
CallDataSize,
|
||||||
|
CallDataCopy,
|
||||||
|
CodeSize,
|
||||||
|
CodeCopy,
|
||||||
|
GasPrice,
|
||||||
|
ExtCodeSize,
|
||||||
|
ExtCodeCopy,
|
||||||
|
ReturnDataSize,
|
||||||
|
ReturnDataCopy,
|
||||||
|
ExtCodeHash,
|
||||||
|
BlockHash,
|
||||||
|
Coinbase,
|
||||||
|
Timestamp,
|
||||||
|
BlockNumber,
|
||||||
|
PrevRanDao,
|
||||||
|
GasLimit,
|
||||||
|
ChainId,
|
||||||
|
SelfBalance,
|
||||||
|
BaseFee,
|
||||||
|
SLoad,
|
||||||
|
SStore,
|
||||||
|
Gas,
|
||||||
|
Create,
|
||||||
|
Create2,
|
||||||
|
Call,
|
||||||
|
StaticCall,
|
||||||
|
DelegateCall,
|
||||||
|
CallCode,
|
||||||
|
Return,
|
||||||
|
Stop,
|
||||||
|
Revert,
|
||||||
|
SelfDestruct,
|
||||||
|
Event,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct SymbolTable {
|
||||||
|
symbols: IndexSet<Symbol>,
|
||||||
|
nonce: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolTable {
|
||||||
|
fn next(&mut self) -> usize {
|
||||||
|
let current = self.nonce;
|
||||||
|
self.nonce += 1;
|
||||||
|
current
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn temporary(&mut self, type_hint: Option<Type>) -> Symbol {
|
||||||
|
let id = self.next();
|
||||||
|
let symbol = Symbol::new(Kind::Temporary(id), type_hint.unwrap_or_default());
|
||||||
|
assert!(self.symbols.insert(symbol));
|
||||||
|
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constant(&mut self, value: U256, type_hint: Option<Type>) -> Symbol {
|
||||||
|
let symbol = Symbol::new(Kind::Constant(value), type_hint.unwrap_or_default());
|
||||||
|
self.symbols.insert(symbol);
|
||||||
|
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn global(&mut self, label: Global) -> Symbol {
|
||||||
|
let symbol = Symbol::global(label);
|
||||||
|
self.symbols.insert(symbol);
|
||||||
|
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
600035565b607f600052610014565b60ff6000525b60206000f3
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
608060405234801561000f575f80fd5b50600436106100f0575f3560e01c806370a082311161009357806395d89b411161006357806395d89b41146101ef578063a9059cbb146101f7578063dd62ed3e1461020a578063f2fde38b14610242575f80fd5b806370a0823114610191578063715018a6146101b957806379cc6790146101c15780638da5cb5b146101d4575f80fd5b806323b872dd116100ce57806323b872dd14610147578063313ce5671461015a57806340c10f191461016957806342966c681461017e575f80fd5b806306fdde03146100f4578063095ea7b31461011257806318160ddd14610135575b5f80fd5b6100fc610255565b604051610109919061078f565b60405180910390f35b6101256101203660046107f6565b6102e5565b6040519015158152602001610109565b6002545b604051908152602001610109565b61012561015536600461081e565b6102fe565b60405160128152602001610109565b61017c6101773660046107f6565b610321565b005b61017c61018c366004610857565b610337565b61013961019f36600461086e565b6001600160a01b03165f9081526020819052604090205490565b61017c610344565b61017c6101cf3660046107f6565b610357565b6005546040516001600160a01b039091168152602001610109565b6100fc61036c565b6101256102053660046107f6565b61037b565b61013961021836600461088e565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b61017c61025036600461086e565b610388565b606060038054610264906108bf565b80601f0160208091040260200160405190810160405280929190818152602001828054610290906108bf565b80156102db5780601f106102b2576101008083540402835291602001916102db565b820191905f5260205f20905b8154815290600101906020018083116102be57829003601f168201915b5050505050905090565b5f336102f28185856103c7565b60019150505b92915050565b5f3361030b8582856103d9565b610316858585610454565b506001949350505050565b6103296104b1565b61033382826104de565b5050565b6103413382610512565b50565b61034c6104b1565b6103555f610546565b565b6103628233836103d9565b6103338282610512565b606060048054610264906108bf565b5f336102f2818585610454565b6103906104b1565b6001600160a01b0381166103be57604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b61034181610546565b6103d48383836001610597565b505050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f19811461044e578181101561044057604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064016103b5565b61044e84848484035f610597565b50505050565b6001600160a01b03831661047d57604051634b637e8f60e11b81525f60048201526024016103b5565b6001600160a01b0382166104a65760405163ec442f0560e01b81525f60048201526024016103b5565b6103d4838383610669565b6005546001600160a01b031633146103555760405163118cdaa760e01b81523360048201526024016103b5565b6001600160a01b0382166105075760405163ec442f0560e01b81525f60048201526024016103b5565b6103335f8383610669565b6001600160a01b03821661053b57604051634b637e8f60e11b81525f60048201526024016103b5565b610333825f83610669565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6001600160a01b0384166105c05760405163e602df0560e01b81525f60048201526024016103b5565b6001600160a01b0383166105e957604051634a1406b160e11b81525f60048201526024016103b5565b6001600160a01b038085165f908152600160209081526040808320938716835292905220829055801561044e57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161065b91815260200190565b60405180910390a350505050565b6001600160a01b038316610693578060025f82825461068891906108f7565b909155506107039050565b6001600160a01b0383165f90815260208190526040902054818110156106e55760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016103b5565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661071f5760028054829003905561073d565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161078291815260200190565b60405180910390a3505050565b5f602080835283518060208501525f5b818110156107bb5785810183015185820160400152820161079f565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b03811681146107f1575f80fd5b919050565b5f8060408385031215610807575f80fd5b610810836107db565b946020939093013593505050565b5f805f60608486031215610830575f80fd5b610839846107db565b9250610847602085016107db565b9150604084013590509250925092565b5f60208284031215610867575f80fd5b5035919050565b5f6020828403121561087e575f80fd5b610887826107db565b9392505050565b5f806040838503121561089f575f80fd5b6108a8836107db565b91506108b6602084016107db565b90509250929050565b600181811c908216806108d357607f821691505b6020821081036108f157634e487b7160e01b5f52602260045260245ffd5b50919050565b808201808211156102f857634e487b7160e01b5f52601160045260245ffdfea26469706673582212203436d2f76da96888f84c631a86a77acc02d0494c4ed226857c2872074984910064736f6c63430008170033
|
||||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
600035600160009160025b818111601c576001019180930191600a565b505060005260206000f350
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
600035600160009160025b818111601c576001019180930191600a565b505060005260206000f350
|
||||||
Reference in New Issue
Block a user