diff --git a/Cargo.toml b/Cargo.toml index 492fefe7..fadbfecb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["crates/ir", "crates/codegen", "crates/object", "crates/parser", "crates/filecheck", "crates/triple"] +members = ["crates/ir", "crates/codegen", "crates/object", "crates/parser", "crates/filecheck", "crates/triple", "crates/interpreter"] diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml new file mode 100644 index 00000000..fad59ff8 --- /dev/null +++ b/crates/interpreter/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "sonatina-interpreter" +version = "0.0.3-alpha" +edition = "2021" +authors = ["Sonatina Developers"] +license = "Apache-2.0" +readme = "../../README.md" +homepage = "https://github.com/fe-lang/sonatina/tree/main/crates/interpreter" +repository = "https://github.com/fe-lang/sonatina" +description = "Interpreter of sonatina intermediate representation" +categories = ["compilers", "wasm"] +keywords = ["compiler", "evm", "wasm", "smart-contract"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +byteorder = { version = "1.4.3", default-features = false } +cranelift-entity = "0.100" +sonatina-ir = { path = "../ir", version = "0.0.3-alpha" } + +[dev-dependencies] +sonatina-parser = { path = "../parser", version = "0.0.3-alpha" } diff --git a/crates/interpreter/src/frame.rs b/crates/interpreter/src/frame.rs new file mode 100644 index 00000000..c985ae18 --- /dev/null +++ b/crates/interpreter/src/frame.rs @@ -0,0 +1,86 @@ +use cranelift_entity::{packed_option::PackedOption, SecondaryMap}; + +use sonatina_ir::{module::ModuleCtx, DataFlowGraph, Type, Value, I256}; + +use crate::{types, EvalValue, ProgramCounter}; + +#[derive(Default)] +pub struct Frame { + pub ret_addr: PackedOption, + local_values: SecondaryMap, // 256-bit register + alloca_region: Vec, // big endian +} + +impl Frame { + pub fn new() -> Self { + Self::default() + } + + pub fn set_ret_addr(&mut self, ret_addr: ProgramCounter) { + self.ret_addr = ret_addr.into(); + } + + pub fn load_args(&mut self, args: &[Value], arg_literals: impl Iterator) { + for (v, literal_value) in args.iter().zip(arg_literals) { + self.local_values[*v] = EvalValue::from_i256(literal_value) + } + } + + pub fn load(&mut self, v: Value, dfg: &DataFlowGraph) -> I256 { + if !self.is_assigned(v) { + if let Some(gv) = dfg.value_gv(v) { + dfg.ctx.with_gv_store(|s| { + if !s.is_const(gv) { + todo!() + } + }) + } + let i256 = dfg.value_imm(v).unwrap().as_i256(); + self.local_values[v] = EvalValue::from_i256(i256); + } + self.local_values[v].i256() + } + + pub fn map(&mut self, literal: I256, v: Value) { + debug_assert!(!self.is_assigned(v)); + self.local_values[v] = EvalValue::from_i256(literal) + } + + pub fn alloca(&mut self, ctx: &ModuleCtx, ty: Type, v: Value) { + debug_assert!(!self.is_assigned(v)); + + let addr = self.alloca_region.len(); + + let size = types::size_of_ty_data(ctx, ty); + self.alloca_region.resize(addr + size, 0); + self.local_values[v] = EvalValue::from_usize(addr); + } + + pub fn ldr(&mut self, ctx: &ModuleCtx, addr: I256, v: Value, ty: Type) { + let addr = addr.to_u256().as_usize(); + debug_assert!(addr < self.alloca_region.len()); + + let size = types::size_of_ty_data(ctx, ty); + let literal_b = &self.alloca_region[addr..addr + size]; + let Some(data) = EvalValue::deserialize(ctx, ty, literal_b) else { + return; + }; + self.map(data.i256(), v); + } + + pub fn str(&mut self, ctx: &ModuleCtx, addr: I256, data: I256, ty: Type) { + let addr = addr.to_u256().as_usize(); + let size = types::size_of_ty_data(ctx, ty); + let reg_value = EvalValue::from_i256(data); + reg_value.serialize(ctx, ty, &mut self.alloca_region[addr..size]); + } + + pub fn is_assigned(&self, v: Value) -> bool { + for (local_v, local) in self.local_values.iter() { + if v == local_v { + return matches!(local, EvalValue::Literal(_)); + } + } + false + } +} diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs new file mode 100644 index 00000000..69190838 --- /dev/null +++ b/crates/interpreter/src/lib.rs @@ -0,0 +1,10 @@ +pub mod frame; +pub mod pc; +pub mod state; +pub mod types; +pub mod value; + +pub use frame::Frame; +pub use pc::ProgramCounter; +pub use state::State; +pub use value::{EvalResult, EvalValue}; diff --git a/crates/interpreter/src/pc.rs b/crates/interpreter/src/pc.rs new file mode 100644 index 00000000..57332d14 --- /dev/null +++ b/crates/interpreter/src/pc.rs @@ -0,0 +1,50 @@ +use cranelift_entity::packed_option::ReservedValue; +use sonatina_ir::{module::FuncRef, Block, Insn, Layout}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ProgramCounter { + pub func_ref: FuncRef, + pub insn: Insn, +} + +impl ReservedValue for ProgramCounter { + fn reserved_value() -> Self { + let func_ref = FuncRef::reserved_value(); + let insn = Insn::reserved_value(); + ProgramCounter { func_ref, insn } + } + + fn is_reserved_value(&self) -> bool { + self.func_ref == FuncRef::reserved_value() && self.insn == Insn::reserved_value() + } +} + +impl ProgramCounter { + pub fn new(entry_func: FuncRef, layout: &Layout) -> Self { + let entry = layout.entry_block().unwrap(); + let insn = layout.first_insn_of(entry).unwrap(); + + Self { + func_ref: entry_func, + insn, + } + } + + pub fn call(&mut self, callee_ref: FuncRef, callee_layout: &Layout) { + *self = ProgramCounter::new(callee_ref, callee_layout) + } + + pub fn next_insn(&mut self, layout: &Layout) { + self.insn = layout.next_insn_of(self.insn).unwrap(); + } + + pub fn branch_to(&mut self, block: Block, layout: &Layout) { + self.insn = layout.first_insn_of(block).unwrap(); + } + + pub fn resume_frame_at(&mut self, ret_addr: Self) { + let ProgramCounter { func_ref, insn } = ret_addr; + self.func_ref = func_ref; + self.insn = insn; + } +} diff --git a/crates/interpreter/src/state.rs b/crates/interpreter/src/state.rs new file mode 100644 index 00000000..71f03998 --- /dev/null +++ b/crates/interpreter/src/state.rs @@ -0,0 +1,575 @@ +use std::ops::{Add, BitAnd, BitOr, BitXor, Mul, Neg, Not, Sub}; + +use sonatina_ir::{ + insn::{BinaryOp, CastOp, UnaryOp}, + module::FuncRef, + Block, DataLocationKind, Immediate, InsnData, Module, Value, +}; + +use crate::{types, EvalResult, Frame, ProgramCounter}; + +pub struct State { + module: Module, + frames: Vec, + pc: ProgramCounter, + prev_block: Option, +} + +impl State { + pub fn new(module: Module, entry_func: FuncRef, args: &[Value]) -> Self { + let func = &module.funcs[entry_func]; + let pc = ProgramCounter::new(entry_func, &func.layout); + + let mut entry_frame = Frame::new(); + debug_assert!(func.arg_values.len() == args.len()); + for arg in args { + entry_frame.load(*arg, &func.dfg); + } + let frames = vec![entry_frame]; + + Self { + module, + frames, + pc, + prev_block: None, + } + } + + pub fn run(mut self) -> EvalResult { + loop { + if let Some(arg) = self.step() { + return arg; + } + } + } + + pub fn step(&mut self) -> Option { + let frame = self.frames.last_mut().unwrap(); + let insn = self.pc.insn; + let ctx = &self.module.ctx; + let func = &self.module.funcs[self.pc.func_ref]; + + let dfg = &func.dfg; + let layout = &func.layout; + + let insn_data = dfg.insn_data(insn); + + use InsnData::*; + match insn_data { + Unary { code, args } => { + let arg = frame.load(args[0], dfg); + use UnaryOp::*; + let result = match code { + Not => arg.not(), + Neg => arg.neg(), + }; + + let v = dfg.insn_result(insn).unwrap(); + frame.map(result, v); + + self.pc.next_insn(layout); + None + } + Binary { code, args } => { + let lhs: Immediate = frame.load(args[0], dfg).into(); + let rhs: Immediate = frame.load(args[1], dfg).into(); + use BinaryOp::*; + let result = match code { + Add => lhs.add(rhs), + Sub => lhs.sub(rhs), + Mul => lhs.mul(rhs), + Udiv => lhs.udiv(rhs), + Sdiv => lhs.sdiv(rhs), + Lt => lhs.lt(rhs), + Gt => lhs.gt(rhs), + Slt => lhs.slt(rhs), + Sgt => lhs.sgt(rhs), + Le => lhs.le(rhs), + Ge => lhs.ge(rhs), + Sle => lhs.sle(rhs), + Sge => lhs.sge(rhs), + Eq => lhs.imm_eq(rhs), + Ne => lhs.imm_ne(rhs), + And => lhs.bitand(rhs), + Or => lhs.bitor(rhs), + Xor => lhs.bitxor(rhs), + } + .as_i256(); + + let v = dfg.insn_result(insn).unwrap(); + frame.map(result, v); + + self.pc.next_insn(layout); + None + } + Cast { code, args, .. } => { + let arg = frame.load(args[0], dfg); + use CastOp::*; + let result = match code { + Zext => arg.neg(), + Sext | Trunc | BitCast => arg, + }; + + let v = dfg.insn_result(insn).unwrap(); + frame.map(result, v); + + self.pc.next_insn(layout); + None + } + Load { args, loc } => { + use DataLocationKind::*; + match loc { + Memory => { + let addr = frame.load(args[0], dfg); + let v = dfg.insn_result(insn).unwrap(); + let ty = dfg.insn_result_ty(insn).unwrap(); + frame.ldr(ctx, addr, v, ty); + } + Storage => todo!(), + } + + self.pc.next_insn(layout); + None + } + Store { args, loc } => { + use DataLocationKind::*; + match loc { + Memory => { + let addr = frame.load(args[0], dfg); + let data = frame.load(args[1], dfg); + let ty = dfg.value_ty(args[1]); + frame.str(ctx, addr, data, ty); + } + Storage => todo!(), + } + + self.pc.next_insn(layout); + None + } + Call { func, args, .. } => { + let arg_literals = args.iter().map(|arg| frame.load(*arg, dfg)); + + // Function prologue + + let ret_addr = self.pc; + + let callee = &self.module.funcs[*func]; + let mut new_frame = Frame::new(); + debug_assert!(callee.arg_values.len() == args.len()); + new_frame.load_args(&callee.arg_values, arg_literals); + new_frame.set_ret_addr(ret_addr); + self.frames.push(new_frame); + + self.pc.call(*func, &callee.layout); + None + } + Jump { dests, .. } => { + let block = layout.insn_block(insn); + self.prev_block = Some(block); + + self.pc.branch_to(dests[0], layout); + None + } + Branch { args, dests } => { + let arg = frame.load(args[0], dfg); + let idx = arg.not().to_u256().as_usize(); + + let block = layout.insn_block(insn); + self.prev_block = Some(block); + self.pc.branch_to(dests[idx], layout); + None + } + BrTable { + args, + default, + table, + } => { + let block = layout.insn_block(insn); + self.prev_block = Some(block); + + let cond = args[0]; + for (idx, arg) in args[1..].iter().enumerate() { + let cond = frame.load(cond, dfg); + let arg = frame.load(*arg, dfg); + if cond == arg { + self.pc.branch_to(table[idx], layout); + return None; + } + } + if let Some(block) = *default { + self.pc.branch_to(block, layout); + } + None + } + Alloca { ty } => { + let v = dfg.insn_result(insn).unwrap(); + frame.alloca(ctx, *ty, v); + + self.pc.next_insn(layout); + None + } + Return { args } => { + let mut frame = self.frames.pop().unwrap(); // pop returning frame + + match self.frames.last_mut() { + Some(caller_frame) => { + // Function epilogue + + self.pc.resume_frame_at(frame.ret_addr.unwrap()); + + let caller = &self.module.funcs[self.pc.func_ref]; + if let Some(arg) = *args { + let arg_literal = frame.load(arg, dfg); + let v = caller.dfg.insn_result(self.pc.insn).unwrap(); + caller_frame.map(arg_literal, v); + } + + self.pc.next_insn(&caller.layout); + None + } + None => { + let Some(arg) = *args else { + return Some(EvalResult::Void); + }; + let arg_literal = frame.load(arg, dfg); + let ty = dfg.value_ty(arg); + Some(EvalResult::from_i256(ctx, arg_literal, ty)) + } + } + } + Gep { args } => { + let mut arg_literals = args.iter().map(|arg| frame.load(*arg, dfg)); + let base_addr = arg_literals.next().unwrap(); + let ty = dfg.value_ty(args[0]); + debug_assert!(ctx.with_ty_store(|s| s.is_ptr(ty))); + + let elem_ptr = types::gep(ctx, base_addr, ty, arg_literals); + + let v = dfg.insn_result(insn).unwrap(); + frame.map(elem_ptr, v); + + self.pc.next_insn(layout); + None + } + Phi { values, blocks, .. } => { + let prev_block = self.prev_block.unwrap(); + for (v, block) in values.iter().zip(blocks.iter()) { + if prev_block == *block { + let lit = frame.load(*v, dfg); + let v = dfg.insn_result(insn).unwrap(); + frame.map(lit, v); + break; + } + } + self.pc.next_insn(layout); + None + } + } + } +} + +#[cfg(test)] +mod test { + use sonatina_parser::parser::Parser; + + use super::*; + + fn parse_module_make_state(input: &str) -> State { + let parser = Parser::default(); + let module = parser.parse(input).unwrap().module; + let func_ref = module.iter_functions().next().unwrap(); + + State::new(module, func_ref, &[]) + } + + #[test] + fn unary() { + let input = " + target = \"evm-ethereum-london\" + + func private %test() -> i32: + block0: + v1.i32 = not 0.i32; + v2.i32 = neg v1; + return v2; + "; + + let state = parse_module_make_state(input); + + let result = state.run(); + + assert_eq!(result.into_i32(), 1i32); + } + + #[test] + fn binary_arithmetic() { + let input = " + target = \"evm-ethereum-london\" + + func private %test() -> i16: + block0: + v0.i16 = add 3.i16 4.i16; + v1.i16 = sub v0 1.i16; + v2.i16 = udiv v1 2.i16; + v3.i8 = sdiv v2 65535.i16; + return v3; + "; + + let state = parse_module_make_state(input); + + let result = state.run(); + + assert_eq!(result.into_i16(), -3i16); + } + + #[test] + fn cast_sext() { + let input = " + target = \"evm-ethereum-london\" + + func private %test() -> i16: + block0: + v0.i16 = sext -128.i8; + return v0; + "; + + let state = parse_module_make_state(input); + + let result = state.run(); + + assert_eq!(result.into_i16(), -128i16); + } + + #[test] + fn cast_zext() { + let input = " + target = \"evm-ethereum-london\" + + func private %test() -> i16: + block0: + v0.i16 = zext -128.i8; + return v0; + "; + + let state = parse_module_make_state(input); + + let elem_ptr = state.run(); + + assert_eq!(elem_ptr.into_i16(), 128i16); + } + + #[test] + fn load_store() { + let input = " + target = \"evm-ethereum-london\" + + func private %test() -> i32: + block0: + v0.*i32 = alloca i32; + store @memory v0 1.i32; + v1.*i32 = load @memory v0; + return v1; + "; + + let state = parse_module_make_state(input); + + let data = state.run(); + + assert_eq!(data.into_i32(), 1i32); + } + + #[test] + fn call() { + let input = " + target = \"evm-ethereum-london\" + + func public %test_callee(v0.i8) -> i8: + block0: + v1.i8 = mul v0 1.i8; + return v1; + + func public %test() -> i8: + block0: + v0.i8 = call %test_callee 0.i8; + return v0; + "; + + let parser = Parser::default(); + let module = parser.parse(input).unwrap().module; + let func_ref = module.iter_functions().nth(1).unwrap(); + + let state = State::new(module, func_ref, &[]); + + let data = state.run(); + + assert_eq!(data.into_i8(), 0i8); + } + + #[test] + fn jump() { + let input = " + target = \"evm-ethereum-london\" + + func private %test() -> i1: + block0: + jump block2; + block1: + return 1.i1; + block2: + return 0.i1; + "; + + let state = parse_module_make_state(input); + + let boolean = state.run(); + + assert!(!boolean.into_bool()) + } + + #[test] + fn branch() { + let input = " + target = \"evm-ethereum-london\" + + func private %test() -> i8: + block0: + br 1.i1 block1 block2; + block1: + return 1.i8; + block2: + return 2.i8; + "; + + let state = parse_module_make_state(input); + + let result = state.run(); + + assert_eq!(result.into_i8(), 1i8); + } + + #[test] + fn br_table() { + let input = " + target = \"evm-ethereum-london\" + + func private %test() -> i64: + block0: + br_table 1.i64 undef (0.i64 block1) (1.i64 block2); + block1: + return 1.i64; + block2: + return 2.i64; + block3: + return 3.i64; + "; + + let state = parse_module_make_state(input); + + let result = state.run(); + + assert_eq!(result.into_i64(), 2i64); + } + + #[test] + fn phi() { + let input = " + target = \"evm-ethereum-london\" + + func private %test() -> i8: + block0: + jump block1; + block1: + jump block2; + block2: + v0.i8 = phi (1.i8 block0) (-1.i8 block1); + return v0; + "; + + let state = parse_module_make_state(input); + + let result = state.run(); + + assert_eq!(result.into_i8(), -1i8); + } + + #[test] + fn gep() { + let input = " + target = \"evm-ethereum-london\" + + type %s1 = {i32, i64, i1}; + + func private %test() -> *i1: + block0: + v0.*%s1 = alloca %s1; + v1.*i1 = gep v0 2.i8; + return v1; + "; + + let state = parse_module_make_state(input); + + let elem_ptr = state.run(); + + assert_eq!(elem_ptr.into_usize(), 12usize); + } + + #[test] + fn ret_void() { + let input = " + target = \"evm-ethereum-london\" + + type %s1 = {i32, i64, i1}; + + func private %test() -> void: + block0: + return; + "; + + let state = parse_module_make_state(input); + + let arg = state.run(); + + arg.into_void(); + } + + #[cfg(target_arch = "aarch64")] + #[test] + fn gep_ptr_ty() { + let input = " + target = \"evm-ethereum-london\" + + func private %test() -> *i1: + block0: + v0.*[*i32; 3] = alloca [*i32; 3]; + v1.*i32 = gep v0 2.i8; + return v1; + "; + + let state = parse_module_make_state(input); + + let elem_ptr = state.run(); + + assert_eq!(elem_ptr.into_usize(), 16usize); + } + + #[test] + fn gep_nested_aggr_ty() { + let input = " + target = \"evm-ethereum-london\" + + type %s1 = {i32, [i16; 3], [i8; 2]}; + + func private %test() -> *i1: + block0: + v0.*%s1 = alloca %s1; + v1.*i1 = gep v0 2.i8 1.i8; + return v1; + "; + + let state = parse_module_make_state(input); + + let elem_ptr = state.run(); + + assert_eq!(elem_ptr.into_usize(), 11usize); + } +} diff --git a/crates/interpreter/src/types.rs b/crates/interpreter/src/types.rs new file mode 100644 index 00000000..45e1ca07 --- /dev/null +++ b/crates/interpreter/src/types.rs @@ -0,0 +1,70 @@ +use std::mem; + +use sonatina_ir::{ + module::ModuleCtx, + types::{CompoundType, CompoundTypeData}, + Type, I256, +}; + +pub fn size_of_ty_data(ctx: &ModuleCtx, ty: Type) -> usize { + match ty { + Type::I1 => mem::size_of::(), + Type::I8 => mem::size_of::(), + Type::I16 => mem::size_of::(), + Type::I32 => mem::size_of::(), + Type::I64 => mem::size_of::(), + Type::I128 => mem::size_of::(), + Type::I256 => 32, + Type::Compound(cmpd_ty) => { + use CompoundTypeData::*; + let cmpd_ty_data = ctx.with_ty_store(|s| s.resolve_compound(cmpd_ty).clone()); + match cmpd_ty_data { + Array { len, elem } => len * size_of_ty_data(ctx, elem), + Ptr(_) => mem::size_of::(), + Struct(data) => data.fields.iter().fold(0usize, |acc, field_ty| { + acc + size_of_ty_data(ctx, *field_ty) + }), + } + } + Type::Void => mem::size_of::<()>(), + } +} + +fn to_cmpd_ty(ty: Type) -> Option { + match ty { + Type::Compound(ty) => Some(ty), + _ => None, + } +} + +pub fn gep( + ctx: &ModuleCtx, + base_addr: I256, + ptr_ty: Type, + args: impl Iterator, +) -> I256 { + let pointee_ty = ctx.with_ty_store(|s| s.deref(ptr_ty)).unwrap(); + debug_assert!(!pointee_ty.is_integral() && !ctx.with_ty_store(|s| s.is_ptr(pointee_ty))); + let mut cmpd_ty = to_cmpd_ty(pointee_ty); + + let mut offset = 0usize; + + for arg in args { + let index = arg.to_u256().as_usize(); + let cmpd_ty_data = ctx.with_ty_store(|s| s.resolve_compound(cmpd_ty.unwrap()).clone()); + match cmpd_ty_data { + CompoundTypeData::Array { elem, .. } => { + offset += index * size_of_ty_data(ctx, elem); + cmpd_ty = to_cmpd_ty(elem); + } + CompoundTypeData::Struct(data) => { + for ty in &data.fields[..index] { + offset += size_of_ty_data(ctx, *ty); + } + cmpd_ty = to_cmpd_ty(data.fields[index]); + } + _ => unreachable!(), + } + } + (base_addr.to_u256().as_usize() + offset).into() +} diff --git a/crates/interpreter/src/value.rs b/crates/interpreter/src/value.rs new file mode 100644 index 00000000..4a2efd0d --- /dev/null +++ b/crates/interpreter/src/value.rs @@ -0,0 +1,164 @@ +use std::mem; + +use byteorder::{BigEndian, WriteBytesExt}; +use sonatina_ir::{module::ModuleCtx, Type, I256, U256}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub enum EvalValue { + Literal(I256), + #[default] + Undefined, +} + +impl EvalValue { + pub fn from_i256(i256: I256) -> Self { + Self::Literal(i256) + } + + pub fn from_usize(addr: usize) -> Self { + Self::Literal(addr.into()) + } + + pub fn i256(&self) -> I256 { + let Self::Literal(i256) = *self else { + panic!("undefined"); + }; + i256 + } + + pub fn deserialize(ctx: &ModuleCtx, ty: Type, b: &[u8]) -> Option { + Some(Self::Literal(match ty { + Type::I1 => (b[0] & 0b1).into(), + Type::I8 => i8::from_be_bytes(b.try_into().unwrap()).into(), + Type::I16 => i16::from_be_bytes(b.try_into().unwrap()).into(), + Type::I32 => i32::from_be_bytes(b.try_into().unwrap()).into(), + Type::I64 => i64::from_be_bytes(b.try_into().unwrap()).into(), + Type::I128 => i128::from_be_bytes(b.try_into().unwrap()).into(), + Type::I256 => I256::from_u256(U256::from_big_endian(b)), + Type::Compound(ty) => { + debug_assert!(ctx.with_ty_store(|s| s.resolve_compound(ty).is_ptr())); + debug_assert!(b.len() == std::mem::size_of::()); + U256::from(usize::from_be_bytes(b.try_into().unwrap())).into() + } + Type::Void => return None, + })) + } + + pub fn serialize(&self, ctx: &ModuleCtx, ty: Type, mut buff: &mut [u8]) { + macro_rules! write_be_bytes { + ($bytes:expr) => {{ + let data = self.i256().trunc_to_i128(); + buff.write_int128::(data, $bytes).unwrap() + }}; + } + + match ty { + Type::I1 => write_be_bytes!(1), + Type::I8 => write_be_bytes!(1), + Type::I16 => write_be_bytes!(2), + Type::I32 => write_be_bytes!(4), + Type::I64 => write_be_bytes!(8), + Type::I128 => write_be_bytes!(16), + Type::I256 => self.i256().to_u256().to_big_endian(buff), + Type::Compound(ty) => { + debug_assert!(ctx.with_ty_store(|s| s.resolve_compound(ty).is_ptr())); + write_be_bytes!(mem::size_of::()); + } + Type::Void => (), + } + } +} + +pub enum EvalResult { + I1(bool), + I8(i8), + I16(i16), + I32(i32), + I64(i64), + I128(i128), + I256(I256), + Void, + Addr(usize), +} + +impl EvalResult { + pub fn from_i256(ctx: &ModuleCtx, i256: I256, ty: Type) -> Self { + use EvalResult::*; + match ty { + Type::I1 => I1(i256.trunc_to_i1()), + Type::I8 => I8(i256.trunc_to_i8()), + Type::I16 => I16(i256.trunc_to_i16()), + Type::I32 => I32(i256.trunc_to_i32()), + Type::I64 => I64(i256.trunc_to_i64()), + Type::I128 => I128(i256.trunc_to_i128()), + Type::I256 => I256(i256), + Type::Compound(_) => { + debug_assert!(ctx.with_ty_store(|s| s.is_ptr(ty))); + Addr(i256.to_u256().as_usize()) + } + _ => unreachable!(), + } + } + + pub fn into_bool(self) -> bool { + let Self::I1(boolean) = self else { + panic!("not a boolean") + }; + boolean + } + + pub fn into_i8(self) -> i8 { + let Self::I8(i8) = self else { + panic!("not an i8") + }; + i8 + } + + pub fn into_i16(self) -> i16 { + let Self::I16(i16) = self else { + panic!("not an i16") + }; + i16 + } + + pub fn into_i32(self) -> i32 { + let Self::I32(i32) = self else { + panic!("not an i32") + }; + i32 + } + + pub fn into_i64(self) -> i64 { + let Self::I64(i64) = self else { + panic!("not an i64") + }; + i64 + } + + pub fn into_i128(self) -> i128 { + let Self::I128(i128) = self else { + panic!("not an i128") + }; + i128 + } + + pub fn into_i256(self) -> I256 { + let Self::I256(i256) = self else { + panic!("not an i256") + }; + i256 + } + + pub fn into_void(self) { + let Self::Void = self else { + panic!("not a void") + }; + } + + pub fn into_usize(self) -> usize { + let Self::Addr(usize) = self else { + panic!("not a memory address") + }; + usize + } +} diff --git a/crates/ir/src/bigint.rs b/crates/ir/src/bigint.rs index f6b8f587..0e00fb98 100644 --- a/crates/ir/src/bigint.rs +++ b/crates/ir/src/bigint.rs @@ -276,3 +276,4 @@ impl_from!(u16, unsigned); impl_from!(u32, unsigned); impl_from!(u64, unsigned); impl_from!(u128, unsigned); +impl_from!(usize, unsigned); diff --git a/crates/ir/src/builder/func_builder.rs b/crates/ir/src/builder/func_builder.rs index a1510828..015298f5 100644 --- a/crates/ir/src/builder/func_builder.rs +++ b/crates/ir/src/builder/func_builder.rs @@ -91,6 +91,10 @@ impl<'a> FunctionBuilder<'a> { self.func_mut().dfg.make_global_value(gv) } + pub fn declare_array_type(&mut self, elem: Type, len: usize) -> Type { + self.module_builder.declare_array_type(elem, len) + } + pub fn declare_struct_type(&mut self, name: &str, fields: &[Type], packed: bool) -> Type { self.module_builder .declare_struct_type(name, fields, packed) @@ -244,6 +248,11 @@ impl<'a> FunctionBuilder<'a> { self.insert_insn(insn_data); } + pub fn gep(&mut self, args: &[Value]) -> Option { + let insn_data = InsnData::Gep { args: args.into() }; + self.insert_insn(insn_data) + } + pub fn phi(&mut self, args: &[(Value, Block)]) -> Value { let ty = self.func().dfg.value_ty(args[0].0); let insn_data = InsnData::Phi { diff --git a/crates/ir/src/builder/module_builder.rs b/crates/ir/src/builder/module_builder.rs index daa84fcd..c3c84a48 100644 --- a/crates/ir/src/builder/module_builder.rs +++ b/crates/ir/src/builder/module_builder.rs @@ -60,6 +60,10 @@ impl ModuleBuilder { .with_ty_store_mut(|s| s.make_struct(name, fields, packed)) } + pub fn declare_array_type(&mut self, elem: Type, len: usize) -> Type { + self.ctx.with_ty_store_mut(|s| s.make_array(elem, len)) + } + pub fn get_func_ref(&self, name: &str) -> Option { self.declared_funcs.get(name).copied() } diff --git a/crates/ir/src/value.rs b/crates/ir/src/value.rs index 6ab9b67e..5f086974 100644 --- a/crates/ir/src/value.rs +++ b/crates/ir/src/value.rs @@ -251,7 +251,7 @@ impl Immediate { self.as_i256().to_u256().as_usize() } - fn from_i256(val: I256, ty: Type) -> Self { + pub fn from_i256(val: I256, ty: Type) -> Self { match ty { Type::I1 => Self::I1(val.trunc_to_i1()), Type::I8 => Self::I8(val.trunc_to_i8()),