diff --git a/crates/interpreter/test_files/insert_value.sntn b/crates/interpreter/test_files/insert_value.sntn new file mode 100644 index 00000000..4bedfa6f --- /dev/null +++ b/crates/interpreter/test_files/insert_value.sntn @@ -0,0 +1,24 @@ +target = "evm-ethereum-london" + +type @s1 = {i32, i64, i1}; + +#[(3.i32, 2.i64, 1.i1) -> 6.i64] +func private %insert_value_basic(v0.i32, v1.i64, v2.i1) -> i64 { + block0: + v3.@s1 = insert_value undef.@s1 0.i256 v0; + v4.@s1 = insert_value v3 1.i256 v1; + v5.@s1 = insert_value v4 2.i256 v2; + + v6.i32 = extract_value v5 0.i256; + v7.i64 = extract_value v5 1.i256; + v8.i1 = extract_value v5 2.i256; + + v9.i64 = sext v6 i64; + v10.i64 = zext v8 i64; + + v11.i64 = add v7 v9; + v12.i64 = add v11 v10; + + return v12; +} + diff --git a/crates/interpreter/tests/common.rs b/crates/interpreter/tests/common.rs index 8c92ab19..aece0c8e 100644 --- a/crates/interpreter/tests/common.rs +++ b/crates/interpreter/tests/common.rs @@ -126,6 +126,7 @@ fn parse_func_cases(module: &ParsedModule, func_ref: FuncRef) -> Result Result { let value = match Parser::parse(Rule::value, input) { Ok(mut pairs) => { diff --git a/crates/ir/src/builder/func_builder.rs b/crates/ir/src/builder/func_builder.rs index d2a0a2e4..94824377 100644 --- a/crates/ir/src/builder/func_builder.rs +++ b/crates/ir/src/builder/func_builder.rs @@ -91,6 +91,10 @@ where self.func.dfg.make_imm_value(imm) } + pub fn make_undef_value(&mut self, ty: Type) -> ValueId { + self.func.dfg.make_undef_value(ty) + } + /// Return pointer value to the global variable. pub fn make_global_value(&mut self, gv: GlobalVariable) -> ValueId { self.func.dfg.make_global_value(gv) diff --git a/crates/ir/src/dfg.rs b/crates/ir/src/dfg.rs index c8b8ae9d..a83389bb 100644 --- a/crates/ir/src/dfg.rs +++ b/crates/ir/src/dfg.rs @@ -75,7 +75,7 @@ impl DataFlowGraph { value } - pub fn make_undef(&mut self, ty: Type) -> ValueId { + pub fn make_undef_value(&mut self, ty: Type) -> ValueId { let value_data = Value::Undef { ty }; self.make_value(value_data) } diff --git a/crates/ir/src/interpret/data.rs b/crates/ir/src/interpret/data.rs index 4e78b9fc..9d43c621 100644 --- a/crates/ir/src/interpret/data.rs +++ b/crates/ir/src/interpret/data.rs @@ -100,6 +100,59 @@ impl Interpret for Alloca { } } +impl Interpret for InsertValue { + fn interpret(&self, state: &mut dyn State) -> EvalValue { + state.set_action(Action::Continue); + + let dest = state.lookup_val(*self.dest()); + let ty = state.dfg().value_ty(*self.dest()); + + let mut fields = match dest { + EvalValue::Aggregate { fields, .. } => fields.clone(), + + EvalValue::Undef => { + let len = match ty.resolve_compound(&state.dfg().ctx).unwrap() { + CompoundTypeData::Array { len, .. } => len, + CompoundTypeData::Struct(s) => s.fields.len(), + CompoundTypeData::Ptr(_) => unreachable!(), + }; + vec![EvalValue::Undef; len] + } + + EvalValue::Imm(_) => { + unreachable!() + } + }; + + let idx = state.lookup_val(*self.idx()).as_imm().unwrap().as_usize(); + fields[idx] = state.lookup_val(*self.value()); + + EvalValue::Aggregate { fields, ty } + } +} + +impl Interpret for ExtractValue { + fn interpret(&self, state: &mut dyn State) -> EvalValue { + state.set_action(Action::Continue); + + let dest = state.lookup_val(*self.dest()); + let dest = match dest { + EvalValue::Aggregate { fields, .. } => fields, + + EvalValue::Undef => { + return EvalValue::Undef; + } + + EvalValue::Imm(_) => { + unreachable!() + } + }; + + let idx = state.lookup_val(*self.idx()).as_imm().unwrap().as_usize(); + dest.into_iter().nth(idx).unwrap() + } +} + fn align_to(offset: usize, alignment: usize) -> usize { assert!(alignment & (alignment - 1) == 0); (offset + alignment - 1) & !(alignment - 1) diff --git a/crates/ir/src/interpret/mod.rs b/crates/ir/src/interpret/mod.rs index 722afe8a..6ed2064f 100644 --- a/crates/ir/src/interpret/mod.rs +++ b/crates/ir/src/interpret/mod.rs @@ -55,6 +55,8 @@ pub trait Interpret { inst::data::Mstore, inst::data::Gep, inst::data::Alloca, + inst::data::InsertValue, + inst::data::ExtractValue, inst::control_flow::Jump, inst::control_flow::Br, inst::control_flow::BrTable, @@ -119,9 +121,13 @@ pub enum Action { Return(EvalValue), } -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq)] pub enum EvalValue { Imm(Immediate), + Aggregate { + fields: Vec, + ty: Type, + }, #[default] Undef, } @@ -134,6 +140,7 @@ impl EvalValue { { match self { EvalValue::Imm(value) => f(value).into(), + EvalValue::Aggregate { .. } => panic!("Type checker needs to handle this!"), EvalValue::Undef => EvalValue::Undef, } } @@ -145,6 +152,8 @@ impl EvalValue { { match (lhs, rhs) { (EvalValue::Imm(l), EvalValue::Imm(r)) => f(l, r).into(), + (EvalValue::Aggregate { .. }, _) => panic!("Type checker needs to handle this!"), + (_, EvalValue::Aggregate { .. }) => panic!("Type checker needs to handle this!"), _ => EvalValue::Undef, } } @@ -165,6 +174,17 @@ impl fmt::Display for EvalValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Imm(imm) => write!(f, "{imm}"), + Self::Aggregate { fields, .. } => { + write!(f, "{{ ")?; + let mut fields = fields.iter(); + if let Some(field) = fields.next() { + write!(f, "{field} ")?; + } + for field in fields { + write!(f, ",{field} ")?; + } + write!(f, " }}") + } Self::Undef => write!(f, "undef"), } } diff --git a/crates/ir/src/types.rs b/crates/ir/src/types.rs index eda869ae..24a4d243 100644 --- a/crates/ir/src/types.rs +++ b/crates/ir/src/types.rs @@ -150,6 +150,14 @@ impl Type { pub fn is_pointer(self, ctx: &ModuleCtx) -> bool { ctx.with_ty_store(|store| store.is_ptr(self)) } + + pub fn resolve_compound(self, ctx: &ModuleCtx) -> Option { + let Self::Compound(cmpd) = self else { + return None; + }; + + Some(ctx.with_ty_store(|s| s.resolve_compound(cmpd).clone())) + } } impl cmp::PartialOrd for Type { @@ -183,7 +191,7 @@ impl cmp::PartialOrd for Type { } impl WriteWithModule for Type { - fn write(&self, module: &ModuleCtx, w: &mut impl io::Write) -> io::Result<()> { + fn write(&self, ctx: &ModuleCtx, w: &mut impl io::Write) -> io::Result<()> { match self { Type::I1 => write!(w, "i1"), Type::I8 => write!(w, "i8"), @@ -192,7 +200,7 @@ impl WriteWithModule for Type { Type::I64 => write!(w, "i64"), Type::I128 => write!(w, "i128"), Type::I256 => write!(w, "i256"), - Type::Compound(cmpd_ty) => cmpd_ty.write(module, w), + Type::Compound(cmpd_ty) => cmpd_ty.write(ctx, w), Type::Unit => write!(w, "unit"), } } @@ -204,16 +212,16 @@ pub struct CompoundType(u32); cranelift_entity::entity_impl!(CompoundType); impl WriteWithModule for CompoundType { - fn write(&self, module: &ModuleCtx, w: &mut impl io::Write) -> io::Result<()> { - module.with_ty_store(|s| match s.resolve_compound(*self) { + fn write(&self, ctx: &ModuleCtx, w: &mut impl io::Write) -> io::Result<()> { + ctx.with_ty_store(|s| match s.resolve_compound(*self) { CompoundTypeData::Array { elem: ty, len } => { write!(w, "[")?; - ty.write(module, &mut *w)?; + ty.write(ctx, &mut *w)?; write!(w, "; {len}]") } CompoundTypeData::Ptr(ty) => { write!(w, "*")?; - ty.write(module, w) + ty.write(ctx, w) } CompoundTypeData::Struct(StructData { name, packed, .. }) => { if *packed {