diff --git a/dialects/hir/src/builders/function.rs b/dialects/hir/src/builders/function.rs index 2b502e3e..cb77b3f9 100644 --- a/dialects/hir/src/builders/function.rs +++ b/dialects/hir/src/builders/function.rs @@ -1,37 +1,35 @@ use midenc_hir2::{ - dialects::builtin::*, AsCallableSymbolRef, Block, BlockRef, Builder, Immediate, Op, OpBuilder, - Overflow, Region, RegionRef, Report, SourceSpan, Type, UnsafeIntrusiveEntityRef, Usable, - ValueRef, + dialects::builtin::*, AsCallableSymbolRef, Block, BlockRef, Builder, Immediate, Listener, + OpBuilder, Overflow, Region, RegionRef, Report, SourceSpan, Type, UnsafeIntrusiveEntityRef, + Usable, ValueRef, }; use crate::*; -pub struct FunctionBuilder<'f> { +pub struct FunctionBuilder<'f, L: Listener> { pub func: &'f mut Function, - builder: OpBuilder, + builder: OpBuilder, } -impl<'f> FunctionBuilder<'f> { - pub fn new(func: &'f mut Function) -> Self { +impl<'f, L: Listener> FunctionBuilder<'f, L> { + pub fn new(func: &'f mut Function, mut builder: OpBuilder) -> Self { let current_block = if func.body().is_empty() { func.create_entry_block() } else { func.last_block() }; - let context = func.as_operation().context_rc(); - let mut builder = OpBuilder::new(context); builder.set_insertion_point_to_end(current_block); Self { func, builder } } - pub fn at(func: &'f mut Function, ip: midenc_hir2::ProgramPoint) -> Self { - let context = func.as_operation().context_rc(); - let mut builder = OpBuilder::new(context); - builder.set_insertion_point(ip); - - Self { func, builder } - } + // pub fn at(func: &'f mut Function, ip: midenc_hir2::ProgramPoint) -> Self { + // let context = func.as_operation().context_rc(); + // let mut builder = OpBuilder::new(context); + // builder.set_insertion_point(ip); + // + // Self { func, builder } + // } pub fn body_region(&self) -> RegionRef { unsafe { RegionRef::from_raw(&*self.func.body()) } @@ -80,44 +78,53 @@ impl<'f> FunctionBuilder<'f> { self.builder.context().append_block_argument(block, ty, span) } - pub fn ins<'a, 'b: 'a>(&'b mut self) -> DefaultInstBuilder<'a> { - DefaultInstBuilder::new(self.func, &mut self.builder) - } -} + // pub fn ins<'a, 'b: 'a>(&'b mut self) -> DefaultInstBuilder<'a, L> { + // DefaultInstBuilder::new(self.func, &mut self.builder) + // } -pub struct DefaultInstBuilder<'f> { - func: &'f mut Function, - builder: &'f mut OpBuilder, -} -impl<'f> DefaultInstBuilder<'f> { - pub(crate) fn new(func: &'f mut Function, builder: &'f mut OpBuilder) -> Self { - Self { func, builder } - } -} -impl<'f> InstBuilderBase<'f> for DefaultInstBuilder<'f> { - fn builder_parts(&mut self) -> (&mut Function, &mut OpBuilder) { - (self.func, self.builder) - } - - fn builder(&self) -> &OpBuilder { - self.builder + pub fn builder(&self) -> &OpBuilder { + &self.builder } - fn builder_mut(&mut self) -> &mut OpBuilder { - self.builder + pub fn builder_mut(&mut self) -> &mut OpBuilder { + &mut self.builder } } +// pub struct DefaultInstBuilder<'f, L: Listener> { +// func: &'f mut Function, +// builder: &'f mut OpBuilder, +// } +// impl<'f, L: Listener> DefaultInstBuilder<'f, L> { +// pub(crate) fn new(func: &'f mut Function, builder: &'f mut OpBuilder) -> Self { +// Self { func, builder } +// } +// } +// impl<'f, L: Listener> InstBuilderBase<'f> for DefaultInstBuilder<'f, L> { +// fn builder_parts(&mut self) -> (&mut Function, &mut OpBuilder) { +// (self.func, self.builder) +// } +// +// fn builder(&self) -> &OpBuilder { +// self.builder +// } +// +// fn builder_mut(&mut self) -> &mut OpBuilder { +// self.builder +// } +// } + pub trait InstBuilderBase<'f>: Sized { - fn builder(&self) -> &OpBuilder; - fn builder_mut(&mut self) -> &mut OpBuilder; - fn builder_parts(&mut self) -> (&mut Function, &mut OpBuilder); - /// Get a default instruction builder using the dataflow graph and insertion point of the - /// current builder - fn ins<'a, 'b: 'a>(&'b mut self) -> DefaultInstBuilder<'a> { - let (func, builder) = self.builder_parts(); - DefaultInstBuilder::new(func, builder) - } + type L: Listener; + fn builder(&self) -> &OpBuilder; + fn builder_mut(&mut self) -> &mut OpBuilder; + // fn builder_parts(&mut self) -> (&mut Function, &mut OpBuilder); + // /// Get a default instruction builder using the dataflow graph and insertion point of the + // /// current builder + // fn ins<'a, 'b: 'a>(&'b mut self) -> DefaultInstBuilder<'a, Self::L> { + // let (func, builder) = self.builder_parts(); + // DefaultInstBuilder::new(func, builder) + // } } pub trait InstBuilder<'f>: InstBuilderBase<'f> { diff --git a/dialects/hir/src/lib.rs b/dialects/hir/src/lib.rs index fd2d2ebc..0959efa8 100644 --- a/dialects/hir/src/lib.rs +++ b/dialects/hir/src/lib.rs @@ -20,7 +20,7 @@ use midenc_hir2::{ }; pub use self::{ - builders::{DefaultInstBuilder, FunctionBuilder, InstBuilder, InstBuilderBase}, + builders::{FunctionBuilder, InstBuilder, InstBuilderBase}, ops::*, }; diff --git a/frontend-wasm2/src/code_translator/mod.rs b/frontend-wasm2/src/code_translator/mod.rs index 05e25017..19a24c49 100644 --- a/frontend-wasm2/src/code_translator/mod.rs +++ b/frontend-wasm2/src/code_translator/mod.rs @@ -13,13 +13,14 @@ //! //! Based on Cranelift's Wasm -> CLIF translator v11.0.0 +use midenc_dialect_hir::InstBuilder; use midenc_hir::{ cranelift_entity::packed_option::ReservedValue, diagnostics::{DiagnosticsHandler, IntoDiagnostic, Report, SourceSpan}, - Block, FieldElement, Immediate, Inst, InstBuilder, Type, + Block, FieldElement, Immediate, Inst, Type, Type::*, - Value, }; +use midenc_hir2::{BlockRef, ValueRef}; use wasmparser::{MemArg, Operator}; use crate::{ @@ -118,7 +119,7 @@ pub fn translate_operator( // https://www.w3.org/TR/wasm-core-1/#-hrefsyntax-instr-parametricmathsfselect%E2%91%A0 // cond is expected to be an i32 let cond_i1 = builder.ins().neq_imm(cond, Immediate::I32(0), span); - state.push1(builder.ins().select(cond_i1, arg1, arg2, span)); + state.push1(builder.ins().select(cond_i1, arg1, arg2, span)?); } Operator::TypedSelect { ty } => { let (arg1, arg2, cond) = state.pop3(); @@ -126,18 +127,18 @@ pub fn translate_operator( wasmparser::ValType::F32 => { let cond = builder.ins().gt_imm(cond, Immediate::Felt(midenc_hir::Felt::ZERO), span); - state.push1(builder.ins().select(cond, arg1, arg2, span)); + state.push1(builder.ins().select(cond, arg1, arg2, span)?); } wasmparser::ValType::I32 => { let cond = builder.ins().neq_imm(cond, Immediate::I32(0), span); - state.push1(builder.ins().select(cond, arg1, arg2, span)); + state.push1(builder.ins().select(cond, arg1, arg2, span)?); } wasmparser::ValType::I64 => { let cond = builder.ins().neq_imm(cond, Immediate::I64(0), span); - state.push1(builder.ins().select(cond, arg1, arg2, span)); + state.push1(builder.ins().select(cond, arg1, arg2, span)?); } ty => panic!("unsupported value type for 'select': {ty}"), - } + }; } Operator::Unreachable => { builder.ins().unreachable(span); @@ -146,13 +147,13 @@ pub fn translate_operator( Operator::Nop => {} /***************************** Control flow blocks *********************************/ Operator::Block { blockty } => { - translate_block(blockty, builder, state, mod_types, diagnostics, span)? + translate_block(blockty, builder, state, mod_types, diagnostics, span)?; } Operator::Loop { blockty } => { - translate_loop(blockty, builder, state, mod_types, diagnostics, span)? + translate_loop(blockty, builder, state, mod_types, diagnostics, span)?; } Operator::If { blockty } => { - translate_if(blockty, state, builder, mod_types, diagnostics, span)? + translate_if(blockty, state, builder, mod_types, diagnostics, span)?; } Operator::Else => translate_else(state, builder, span)?, Operator::End => translate_end(state, builder, span), @@ -184,11 +185,11 @@ pub fn translate_operator( /******************************* Memory management *********************************/ Operator::MemoryGrow { .. } => { let arg = state.pop1_bitcasted(U32, builder, span); - state.push1(builder.ins().mem_grow(arg, span)); + state.push1(builder.ins().mem_grow(arg, span)?); } Operator::MemorySize { .. } => { // Return total Miden memory size - state.push1(builder.ins().mem_size(span)); + state.push1(builder.ins().mem_size(span)?); } /******************************* Bulk memory operations *********************************/ Operator::MemoryCopy { dst_mem, src_mem } => { @@ -197,7 +198,7 @@ pub fn translate_operator( let count_i32 = state.pop1(); let src_i32 = state.pop1(); let dst_i32 = state.pop1(); - let count = builder.ins().bitcast(count_i32, Type::U32, span); + let count = builder.ins().bitcast(count_i32, Type::U32, span)?; let dst = prepare_addr(dst_i32, &U8, None, builder, span); let src = prepare_addr(src_i32, &U8, None, builder, span); builder.ins().memcpy(src, dst, count, span); @@ -213,56 +214,56 @@ pub fn translate_operator( let num_bytes = state.pop1(); let value = state.pop1(); let dst_i32 = state.pop1(); - let value = builder.ins().trunc(value, Type::U8, span); - let num_bytes = builder.ins().bitcast(num_bytes, Type::U32, span); + let value = builder.ins().trunc(value, Type::U8, span)?; + let num_bytes = builder.ins().bitcast(num_bytes, Type::U32, span)?; let dst = prepare_addr(dst_i32, &U8, None, builder, span); builder.ins().memset(dst, num_bytes, value, span); } /******************************* Load instructions ***********************************/ Operator::I32Load8U { memarg } => { - translate_load_zext(U8, I32, memarg, state, builder, span) + translate_load_zext(U8, I32, memarg, state, builder, span)?; } Operator::I32Load16U { memarg } => { - translate_load_zext(U16, I32, memarg, state, builder, span) + translate_load_zext(U16, I32, memarg, state, builder, span)?; } Operator::I32Load8S { memarg } => { - translate_load_sext(I8, I32, memarg, state, builder, span); + translate_load_sext(I8, I32, memarg, state, builder, span)?; } Operator::I32Load16S { memarg } => { - translate_load_sext(I16, I32, memarg, state, builder, span); + translate_load_sext(I16, I32, memarg, state, builder, span)?; } Operator::I64Load8U { memarg } => { - translate_load_zext(U8, I64, memarg, state, builder, span) + translate_load_zext(U8, I64, memarg, state, builder, span)?; } Operator::I64Load16U { memarg } => { - translate_load_zext(U16, I64, memarg, state, builder, span) + translate_load_zext(U16, I64, memarg, state, builder, span)?; } Operator::I64Load8S { memarg } => { - translate_load_sext(I8, I64, memarg, state, builder, span); + translate_load_sext(I8, I64, memarg, state, builder, span)?; } Operator::I64Load16S { memarg } => { - translate_load_sext(I16, I64, memarg, state, builder, span); + translate_load_sext(I16, I64, memarg, state, builder, span)?; } Operator::I64Load32S { memarg } => { - translate_load_sext(I32, I64, memarg, state, builder, span) + translate_load_sext(I32, I64, memarg, state, builder, span)?; } Operator::I64Load32U { memarg } => { - translate_load_zext(U32, I64, memarg, state, builder, span) + translate_load_zext(U32, I64, memarg, state, builder, span)?; } Operator::I32Load { memarg } => translate_load(I32, memarg, state, builder, span), Operator::I64Load { memarg } => translate_load(I64, memarg, state, builder, span), Operator::F32Load { memarg } => translate_load(Felt, memarg, state, builder, span), /****************************** Store instructions ***********************************/ - Operator::I32Store { memarg } => translate_store(I32, memarg, state, builder, span), - Operator::I64Store { memarg } => translate_store(I64, memarg, state, builder, span), - Operator::F32Store { memarg } => translate_store(Felt, memarg, state, builder, span), + Operator::I32Store { memarg } => translate_store(I32, memarg, state, builder, span)?, + Operator::I64Store { memarg } => translate_store(I64, memarg, state, builder, span)?, + Operator::F32Store { memarg } => translate_store(Felt, memarg, state, builder, span)?, Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => { translate_store(U8, memarg, state, builder, span); } Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => { - translate_store(U16, memarg, state, builder, span); + translate_store(U16, memarg, state, builder, span)?; } - Operator::I64Store32 { memarg } => translate_store(U32, memarg, state, builder, span), + Operator::I64Store32 { memarg } => translate_store(U32, memarg, state, builder, span)?, /****************************** Nullary Operators **********************************/ Operator::I32Const { value } => state.push1(builder.ins().i32(*value, span)), Operator::I64Const { value } => state.push1(builder.ins().i64(*value, span)), @@ -270,40 +271,40 @@ pub fn translate_operator( /******************************* Unary Operators *************************************/ Operator::I32Clz | Operator::I64Clz => { let val = state.pop1(); - let count = builder.ins().clz(val, span); + let count = builder.ins().clz(val, span)?; // To ensure we match the Wasm semantics, treat the output of clz as an i32 - state.push1(builder.ins().bitcast(count, Type::I32, span)); + state.push1(builder.ins().bitcast(count, Type::I32, span)?); } Operator::I32Ctz | Operator::I64Ctz => { let val = state.pop1(); - let count = builder.ins().ctz(val, span); + let count = builder.ins().ctz(val, span)?; // To ensure we match the Wasm semantics, treat the output of ctz as an i32 - state.push1(builder.ins().bitcast(count, Type::I32, span)); + state.push1(builder.ins().bitcast(count, Type::I32, span)?); } Operator::I32Popcnt | Operator::I64Popcnt => { let val = state.pop1(); - let count = builder.ins().popcnt(val, span); + let count = builder.ins().popcnt(val, span)?; // To ensure we match the Wasm semantics, treat the output of popcnt as an i32 - state.push1(builder.ins().bitcast(count, Type::I32, span)); + state.push1(builder.ins().bitcast(count, Type::I32, span)?); } Operator::I32Extend8S | Operator::I32Extend16S => { let val = state.pop1(); - state.push1(builder.ins().sext(val, I32, span)); + state.push1(builder.ins().sext(val, I32, span)?); } Operator::I64ExtendI32S => { let val = state.pop1(); - state.push1(builder.ins().sext(val, I64, span)); + state.push1(builder.ins().sext(val, I64, span)?); } Operator::I64ExtendI32U => { let val = state.pop1(); - let u32_val = builder.ins().bitcast(val, U32, span); - let u64_val = builder.ins().zext(u32_val, U64, span); - let i64_val = builder.ins().bitcast(u64_val, I64, span); + let u32_val = builder.ins().bitcast(val, U32, span)?; + let u64_val = builder.ins().zext(u32_val, U64, span)?; + let i64_val = builder.ins().bitcast(u64_val, I64, span)?; state.push1(i64_val); } Operator::I32WrapI64 => { let val = state.pop1(); - state.push1(builder.ins().trunc(val, I32, span)); + state.push1(builder.ins().trunc(val, I32, span)?); } /****************************** Binary Operators ************************************/ Operator::I32Add | Operator::I64Add => { @@ -313,123 +314,123 @@ pub fn translate_operator( let value_type = builder.data_flow_graph().value_type(arg1); let arg2 = if value_type != builder.data_flow_graph().value_type(arg2) { let value_type = value_type.clone(); - builder.ins().bitcast(arg2, value_type, span) + builder.ins().bitcast(arg2, value_type, span)? } else { arg2 }; - state.push1(builder.ins().add_wrapping(arg1, arg2, span)); + state.push1(builder.ins().add_wrapping(arg1, arg2, span)?); } Operator::I32And | Operator::I64And => { let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().band(arg1, arg2, span)); + state.push1(builder.ins().band(arg1, arg2, span)?); } Operator::I32Or | Operator::I64Or => { let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().bor(arg1, arg2, span)); + state.push1(builder.ins().bor(arg1, arg2, span)?); } Operator::I32Xor | Operator::I64Xor => { let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().bxor(arg1, arg2, span)); + state.push1(builder.ins().bxor(arg1, arg2, span)?); } Operator::I32Shl => { let (arg1, arg2) = state.pop2(); // wrapping shift semantics drop any bits that would cause // the shift to exceed the bitwidth of the type - let arg2 = builder.ins().bitcast(arg2, U32, span); - state.push1(builder.ins().shl(arg1, arg2, span)); + let arg2 = builder.ins().bitcast(arg2, U32, span)?; + state.push1(builder.ins().shl(arg1, arg2, span)?); } Operator::I64Shl => { let (arg1, arg2) = state.pop2(); // wrapping shift semantics drop any bits that would cause // the shift to exceed the bitwidth of the type - let arg2 = builder.ins().cast(arg2, U32, span); - state.push1(builder.ins().shl(arg1, arg2, span)); + let arg2 = builder.ins().cast(arg2, U32, span)?; + state.push1(builder.ins().shl(arg1, arg2, span)?); } Operator::I32ShrU => { let (arg1, arg2) = state.pop2_bitcasted(U32, builder, span); // wrapping shift semantics drop any bits that would cause // the shift to exceed the bitwidth of the type - let val = builder.ins().shr(arg1, arg2, span); - state.push1(builder.ins().bitcast(val, I32, span)); + let val = builder.ins().shr(arg1, arg2, span)?; + state.push1(builder.ins().bitcast(val, I32, span)?); } Operator::I64ShrU => { let (arg1, arg2) = state.pop2(); - let arg1 = builder.ins().bitcast(arg1, U64, span); - let arg2 = builder.ins().cast(arg2, U32, span); + let arg1 = builder.ins().bitcast(arg1, U64, span)?; + let arg2 = builder.ins().cast(arg2, U32, span)?; // wrapping shift semantics drop any bits that would cause // the shift to exceed the bitwidth of the type - let val = builder.ins().shr(arg1, arg2, span); - state.push1(builder.ins().bitcast(val, I64, span)); + let val = builder.ins().shr(arg1, arg2, span)?; + state.push1(builder.ins().bitcast(val, I64, span)?); } Operator::I32ShrS => { let (arg1, arg2) = state.pop2(); // wrapping shift semantics drop any bits that would cause // the shift to exceed the bitwidth of the type - let arg2 = builder.ins().bitcast(arg2, Type::U32, span); - state.push1(builder.ins().shr(arg1, arg2, span)); + let arg2 = builder.ins().bitcast(arg2, Type::U32, span)?; + state.push1(builder.ins().shr(arg1, arg2, span)?); } Operator::I64ShrS => { let (arg1, arg2) = state.pop2(); // wrapping shift semantics drop any bits that would cause // the shift to exceed the bitwidth of the type - let arg2 = builder.ins().cast(arg2, Type::U32, span); - state.push1(builder.ins().shr(arg1, arg2, span)); + let arg2 = builder.ins().cast(arg2, Type::U32, span)?; + state.push1(builder.ins().shr(arg1, arg2, span)?); } Operator::I32Rotl => { let (arg1, arg2) = state.pop2(); - let arg2 = builder.ins().bitcast(arg2, Type::U32, span); - state.push1(builder.ins().rotl(arg1, arg2, span)); + let arg2 = builder.ins().bitcast(arg2, Type::U32, span)?; + state.push1(builder.ins().rotl(arg1, arg2, span)?); } Operator::I64Rotl => { let (arg1, arg2) = state.pop2(); - let arg2 = builder.ins().cast(arg2, Type::U32, span); - state.push1(builder.ins().rotl(arg1, arg2, span)); + let arg2 = builder.ins().cast(arg2, Type::U32, span)?; + state.push1(builder.ins().rotl(arg1, arg2, span)?); } Operator::I32Rotr => { let (arg1, arg2) = state.pop2(); - let arg2 = builder.ins().bitcast(arg2, Type::U32, span); - state.push1(builder.ins().rotr(arg1, arg2, span)); + let arg2 = builder.ins().bitcast(arg2, Type::U32, span)?; + state.push1(builder.ins().rotr(arg1, arg2, span)?); } Operator::I64Rotr => { let (arg1, arg2) = state.pop2(); - let arg2 = builder.ins().cast(arg2, Type::U32, span); - state.push1(builder.ins().rotr(arg1, arg2, span)); + let arg2 = builder.ins().cast(arg2, Type::U32, span)?; + state.push1(builder.ins().rotr(arg1, arg2, span)?); } Operator::I32Sub | Operator::I64Sub => { let (arg1, arg2) = state.pop2(); // wrapping because the result is mod 2^N // https://www.w3.org/TR/wasm-core-1/#op-isub - state.push1(builder.ins().sub_wrapping(arg1, arg2, span)); + state.push1(builder.ins().sub_wrapping(arg1, arg2, span)?); } Operator::I32Mul | Operator::I64Mul => { let (arg1, arg2) = state.pop2(); // wrapping because the result is mod 2^N // https://www.w3.org/TR/wasm-core-1/#op-imul - state.push1(builder.ins().mul_wrapping(arg1, arg2, span)); + state.push1(builder.ins().mul_wrapping(arg1, arg2, span)?); } Operator::I32DivS | Operator::I64DivS => { let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().div_unchecked(arg1, arg2, span)); + state.push1(builder.ins().div_unchecked(arg1, arg2, span)?); } Operator::I32DivU => { let (arg1, arg2) = state.pop2_bitcasted(U32, builder, span); let val = builder.ins().div_unchecked(arg1, arg2, span); - state.push1(builder.ins().bitcast(val, I32, span)); + state.push1(builder.ins().bitcast(val, I32, span)?); } Operator::I64DivU => { let (arg1, arg2) = state.pop2_bitcasted(U64, builder, span); let val = builder.ins().div_unchecked(arg1, arg2, span); - state.push1(builder.ins().bitcast(val, I64, span)); + state.push1(builder.ins().bitcast(val, I64, span)?); } Operator::I32RemU => { let (arg1, arg2) = state.pop2_bitcasted(U32, builder, span); let val = builder.ins().r#mod_checked(arg1, arg2, span); - state.push1(builder.ins().bitcast(val, I32, span)); + state.push1(builder.ins().bitcast(val, I32, span)?); } Operator::I64RemU => { let (arg1, arg2) = state.pop2_bitcasted(U64, builder, span); let val = builder.ins().r#mod_checked(arg1, arg2, span); - state.push1(builder.ins().bitcast(val, I64, span)); + state.push1(builder.ins().bitcast(val, I64, span)?); } Operator::I32RemS | Operator::I64RemS => { let (arg1, arg2) = state.pop2(); @@ -438,108 +439,108 @@ pub fn translate_operator( /**************************** Comparison Operators **********************************/ Operator::I32LtU => { let (arg0, arg1) = state.pop2_bitcasted(U32, builder, span); - let val = builder.ins().lt(arg0, arg1, span); - state.push1(builder.ins().sext(val, I32, span)); + let val = builder.ins().lt(arg0, arg1, span)?; + state.push1(builder.ins().sext(val, I32, span)?); } Operator::I64LtU => { let (arg0, arg1) = state.pop2_bitcasted(U64, builder, span); - let val = builder.ins().lt(arg0, arg1, span); - state.push1(builder.ins().sext(val, I32, span)); + let val = builder.ins().lt(arg0, arg1, span)?; + state.push1(builder.ins().sext(val, I32, span)?); } Operator::I32LtS => { let (arg0, arg1) = state.pop2(); - let val = builder.ins().lt(arg0, arg1, span); - state.push1(builder.ins().sext(val, I32, span)); + let val = builder.ins().lt(arg0, arg1, span)?; + state.push1(builder.ins().sext(val, I32, span)?); } Operator::I64LtS => { let (arg0, arg1) = state.pop2(); - let val = builder.ins().lt(arg0, arg1, span); - state.push1(builder.ins().sext(val, I32, span)); + let val = builder.ins().lt(arg0, arg1, span)?; + state.push1(builder.ins().sext(val, I32, span)?); } Operator::I32LeU => { let (arg0, arg1) = state.pop2_bitcasted(U32, builder, span); - let val = builder.ins().lte(arg0, arg1, span); - state.push1(builder.ins().sext(val, I32, span)); + let val = builder.ins().lte(arg0, arg1, span)?; + state.push1(builder.ins().sext(val, I32, span)?); } Operator::I64LeU => { let (arg0, arg1) = state.pop2_bitcasted(U64, builder, span); - let val = builder.ins().lte(arg0, arg1, span); - state.push1(builder.ins().sext(val, I32, span)); + let val = builder.ins().lte(arg0, arg1, span)?; + state.push1(builder.ins().sext(val, I32, span)?); } Operator::I32LeS => { let (arg0, arg1) = state.pop2(); - let val = builder.ins().lte(arg0, arg1, span); - state.push1(builder.ins().sext(val, I32, span)); + let val = builder.ins().lte(arg0, arg1, span)?; + state.push1(builder.ins().sext(val, I32, span)?); } Operator::I64LeS => { let (arg0, arg1) = state.pop2(); - let val = builder.ins().lte(arg0, arg1, span); - state.push1(builder.ins().sext(val, I32, span)); + let val = builder.ins().lte(arg0, arg1, span)?; + state.push1(builder.ins().sext(val, I32, span)?); } Operator::I32GtU => { let (arg0, arg1) = state.pop2_bitcasted(U32, builder, span); - let val = builder.ins().gt(arg0, arg1, span); - state.push1(builder.ins().sext(val, I32, span)); + let val = builder.ins().gt(arg0, arg1, span)?; + state.push1(builder.ins().sext(val, I32, span)?); } Operator::I64GtU => { let (arg0, arg1) = state.pop2_bitcasted(U64, builder, span); - let val = builder.ins().gt(arg0, arg1, span); - state.push1(builder.ins().sext(val, I32, span)); + let val = builder.ins().gt(arg0, arg1, span)?; + state.push1(builder.ins().sext(val, I32, span)?); } Operator::I32GtS | Operator::I64GtS => { let (arg0, arg1) = state.pop2(); - let val = builder.ins().gt(arg0, arg1, span); - state.push1(builder.ins().zext(val, I32, span)); + let val = builder.ins().gt(arg0, arg1, span)?; + state.push1(builder.ins().zext(val, I32, span)?); } Operator::I32GeU => { let (arg0, arg1) = state.pop2_bitcasted(U32, builder, span); - let val = builder.ins().gte(arg0, arg1, span); - state.push1(builder.ins().zext(val, I32, span)); + let val = builder.ins().gte(arg0, arg1, span)?; + state.push1(builder.ins().zext(val, I32, span)?); } Operator::I64GeU => { let (arg0, arg1) = state.pop2_bitcasted(U64, builder, span); - let val = builder.ins().gte(arg0, arg1, span); - state.push1(builder.ins().zext(val, I32, span)); + let val = builder.ins().gte(arg0, arg1, span)?; + state.push1(builder.ins().zext(val, I32, span)?); } Operator::I32GeS => { let (arg0, arg1) = state.pop2(); - let val = builder.ins().gte(arg0, arg1, span); - state.push1(builder.ins().zext(val, I32, span)); + let val = builder.ins().gte(arg0, arg1, span)?; + state.push1(builder.ins().zext(val, I32, span)?); } Operator::I64GeS => { let (arg0, arg1) = state.pop2(); - let val = builder.ins().gte(arg0, arg1, span); - state.push1(builder.ins().zext(val, I32, span)); + let val = builder.ins().gte(arg0, arg1, span)?; + state.push1(builder.ins().zext(val, I32, span)?); } Operator::I32Eqz => { let arg = state.pop1(); let val = builder.ins().eq_imm(arg, Immediate::I32(0), span); - state.push1(builder.ins().zext(val, I32, span)); + state.push1(builder.ins().zext(val, I32, span)?); } Operator::I64Eqz => { let arg = state.pop1(); let val = builder.ins().eq_imm(arg, Immediate::I64(0), span); - state.push1(builder.ins().zext(val, I32, span)); + state.push1(builder.ins().zext(val, I32, span)?); } Operator::I32Eq => { let (arg0, arg1) = state.pop2(); - let val = builder.ins().eq(arg0, arg1, span); - state.push1(builder.ins().zext(val, I32, span)); + let val = builder.ins().eq(arg0, arg1, span)?; + state.push1(builder.ins().zext(val, I32, span)?); } Operator::I64Eq => { let (arg0, arg1) = state.pop2(); - let val = builder.ins().eq(arg0, arg1, span); - state.push1(builder.ins().zext(val, I32, span)); + let val = builder.ins().eq(arg0, arg1, span)?; + state.push1(builder.ins().zext(val, I32, span)?); } Operator::I32Ne => { let (arg0, arg1) = state.pop2(); - let val = builder.ins().neq(arg0, arg1, span); - state.push1(builder.ins().zext(val, I32, span)); + let val = builder.ins().neq(arg0, arg1, span)?; + state.push1(builder.ins().zext(val, I32, span)?); } Operator::I64Ne => { let (arg0, arg1) = state.pop2(); - let val = builder.ins().neq(arg0, arg1, span); - state.push1(builder.ins().zext(val, I32, span)); + let val = builder.ins().neq(arg0, arg1, span)?; + state.push1(builder.ins().zext(val, I32, span)?); } op => { unsupported_diag!(diagnostics, "Wasm op {:?} is not supported", op); @@ -567,12 +568,13 @@ fn translate_load_sext( state: &mut FuncTranslationState, builder: &mut FunctionBuilderExt, span: SourceSpan, -) { +) -> WasmResult<()> { let addr_int = state.pop1(); let addr = prepare_addr(addr_int, &ptr_ty, Some(memarg), builder, span); let val = builder.ins().load(addr, span); - let sext_val = builder.ins().sext(val, sext_ty, span); + let sext_val = builder.ins().sext(val, sext_ty, span)?; state.push1(sext_val); + Ok(()) } fn translate_load_zext( @@ -582,13 +584,14 @@ fn translate_load_zext( state: &mut FuncTranslationState, builder: &mut FunctionBuilderExt, span: SourceSpan, -) { +) -> WasmResult<()> { assert!(ptr_ty.is_unsigned_integer()); let addr_int = state.pop1(); let addr = prepare_addr(addr_int, &ptr_ty, Some(memarg), builder, span); let val = builder.ins().load(addr, span); - let sext_val = builder.ins().zext(val, zext_ty, span); + let sext_val = builder.ins().zext(val, zext_ty, span)?; state.push1(sext_val); + Ok(()) } fn translate_store( @@ -597,40 +600,41 @@ fn translate_store( state: &mut FuncTranslationState, builder: &mut FunctionBuilderExt, span: SourceSpan, -) { +) -> WasmResult<()> { let (addr_int, val) = state.pop2(); let val_ty = builder.data_flow_graph().value_type(val); let arg = if &ptr_ty != val_ty { if ptr_ty.size_in_bits() == val_ty.size_in_bits() { - builder.ins().bitcast(val, ptr_ty.clone(), span) + builder.ins().bitcast(val, ptr_ty.clone(), span)? } else if ptr_ty.is_unsigned_integer() && val_ty.is_signed_integer() { let unsigned_val_ty = val_ty.as_unsigned(); - let uval = builder.ins().bitcast(val, unsigned_val_ty, span); - builder.ins().trunc(uval, ptr_ty.clone(), span) + let uval = builder.ins().bitcast(val, unsigned_val_ty, span)?; + builder.ins().trunc(uval, ptr_ty.clone(), span)? } else { - builder.ins().trunc(val, ptr_ty.clone(), span) + builder.ins().trunc(val, ptr_ty.clone(), span)? } } else { val }; let addr = prepare_addr(addr_int, &ptr_ty, Some(memarg), builder, span); builder.ins().store(addr, arg, span); + Ok(()) } fn prepare_addr( - addr_int: Value, + addr_int: ValueRef, ptr_ty: &Type, memarg: Option<&MemArg>, builder: &mut FunctionBuilderExt, span: SourceSpan, -) -> Value { +) -> WasmResult { let addr_int_ty = builder.data_flow_graph().value_type(addr_int); let addr_u32 = if addr_int_ty == &U32 { addr_int } else if addr_int_ty == &I32 { - builder.ins().bitcast(addr_int, U32, span) + builder.ins().bitcast(addr_int, U32, span)? } else if matches!(addr_int_ty, Ptr(_)) { - builder.ins().ptrtoint(addr_int, U32, span) + builder.ins().ptrtoint(addr_int, U32, span)? } else { panic!("unexpected type used as pointer value: {addr_int_ty}"); }; @@ -784,7 +788,7 @@ fn translate_br_if( fn translate_br_if_args( relative_depth: u32, state: &mut FuncTranslationState, -) -> (Block, &mut [Value]) { +) -> (BlockRef, &mut [ValueRef]) { let i = state.control_stack.len() - 1 - (relative_depth as usize); let (return_count, br_destination) = { let frame = &mut state.control_stack[i]; @@ -839,7 +843,7 @@ fn translate_br_table( let val = state.pop1(); let val = if builder.data_flow_graph().value_type(val) != &U32 { - builder.ins().cast(val, U32, span) + builder.ins().cast(val, U32, span)? } else { val }; diff --git a/frontend-wasm2/src/code_translator/tests_unsupported.rs b/frontend-wasm2/src/code_translator/tests_unsupported.rs index 8e981fb8..91baf0b9 100644 --- a/frontend-wasm2/src/code_translator/tests_unsupported.rs +++ b/frontend-wasm2/src/code_translator/tests_unsupported.rs @@ -13,35 +13,36 @@ use crate::{ }; fn check_unsupported(op: &Operator) { - let context = test_context(); - let mod_name = "noname"; - let module_info = Module::default(); - let mut module_builder = ModuleBuilder::new(mod_name); - let sig = Signature { - params: vec![], - results: vec![], - cc: CallConv::SystemV, - linkage: Linkage::External, - }; - let mut module_func_builder = module_builder.function("func_name", sig.clone()).unwrap(); - let mut fb_ctx = FunctionBuilderContext::new(); - let mod_types = Default::default(); - let mut state = FuncTranslationState::new(); - let mut builder_ext = FunctionBuilderExt::new(&mut module_func_builder, &mut fb_ctx); - let mut module_state = - ModuleTranslationState::new(&module_info, &mod_types, vec![], &context.session.diagnostics); - let result = translate_operator( - op, - &mut builder_ext, - &mut state, - &mut module_state, - &module_info, - &mod_types, - &context.session.diagnostics, - SourceSpan::default(), - ); - assert!(result.is_err(), "Expected unsupported op error for {:?}", op); - assert_eq!(result.unwrap_err().to_string(), format!("Wasm op {:?} is not supported", op)); + todo!() + // let context = test_context(); + // let mod_name = "noname"; + // let module_info = Module::default(); + // let mut module_builder = ModuleBuilder::new(mod_name); + // let sig = Signature { + // params: vec![], + // results: vec![], + // cc: CallConv::SystemV, + // linkage: Linkage::External, + // }; + // let mut module_func_builder = module_builder.function("func_name", sig.clone()).unwrap(); + // let mut fb_ctx = FunctionBuilderContext::new(); + // let mod_types = Default::default(); + // let mut state = FuncTranslationState::new(); + // let mut builder_ext = FunctionBuilderExt::new(&mut module_func_builder, &mut fb_ctx); + // let mut module_state = + // ModuleTranslationState::new(&module_info, &mod_types, vec![], &context.session.diagnostics); + // let result = translate_operator( + // op, + // &mut builder_ext, + // &mut state, + // &mut module_state, + // &module_info, + // &mod_types, + // &context.session.diagnostics, + // SourceSpan::default(), + // ); + // assert!(result.is_err(), "Expected unsupported op error for {:?}", op); + // assert_eq!(result.unwrap_err().to_string(), format!("Wasm op {:?} is not supported", op)); } // Wasm Spec v1.0 diff --git a/frontend-wasm2/src/intrinsics/mod.rs b/frontend-wasm2/src/intrinsics/mod.rs index fdadf611..26bf7fc6 100644 --- a/frontend-wasm2/src/intrinsics/mod.rs +++ b/frontend-wasm2/src/intrinsics/mod.rs @@ -4,6 +4,7 @@ pub mod mem; use std::{collections::HashSet, sync::OnceLock}; use midenc_hir::{FunctionIdent, FunctionType, SourceSpan, Symbol, Value}; +use midenc_hir2::ValueRef; use crate::module::function_builder_ext::FunctionBuilderExt; @@ -25,10 +26,10 @@ fn modules() -> &'static HashSet<&'static str> { /// Convert a call to a Miden intrinsic function into instruction(s) pub fn convert_intrinsics_call( func_id: FunctionIdent, - args: &[Value], + args: &[ValueRef], builder: &mut FunctionBuilderExt, span: SourceSpan, -) -> Vec { +) -> Vec { match func_id.module.as_symbol().as_str() { mem::MODULE_ID => mem::convert_mem_intrinsics(func_id, args, builder, span), felt::MODULE_ID => felt::convert_felt_intrinsics(func_id, args, builder, span), diff --git a/frontend-wasm2/src/miden_abi/transform.rs b/frontend-wasm2/src/miden_abi/transform.rs index 9a77accb..3e9b298a 100644 --- a/frontend-wasm2/src/miden_abi/transform.rs +++ b/frontend-wasm2/src/miden_abi/transform.rs @@ -2,8 +2,8 @@ use midenc_hir::{ diagnostics::{DiagnosticsHandler, SourceSpan}, FunctionIdent, Immediate, InstBuilder, Type::*, - Value, }; +use midenc_hir2::ValueRef; use super::{stdlib, tx_kernel}; use crate::module::function_builder_ext::FunctionBuilderExt; @@ -64,11 +64,11 @@ fn get_transform_strategy(module_id: &str, function_id: &str) -> TransformStrate /// Transform a function call based on the transformation strategy pub fn transform_miden_abi_call( func_id: FunctionIdent, - args: &[Value], + args: &[ValueRef], builder: &mut FunctionBuilderExt, span: SourceSpan, diagnostics: &DiagnosticsHandler, -) -> Vec { +) -> Vec { use TransformStrategy::*; match get_transform_strategy(func_id.module.as_str(), func_id.function.as_str()) { ListReturn => list_return(func_id, args, builder, span, diagnostics), @@ -81,11 +81,11 @@ pub fn transform_miden_abi_call( #[inline(always)] pub fn no_transform( func_id: FunctionIdent, - args: &[Value], + args: &[ValueRef], builder: &mut FunctionBuilderExt, span: SourceSpan, _diagnostics: &DiagnosticsHandler, -) -> Vec { +) -> Vec { let call = builder.ins().exec(func_id, args, span); let results = builder.inst_results(call); results.to_vec() @@ -94,11 +94,11 @@ pub fn no_transform( /// The Miden ABI function returns a length and a pointer and we only want the length pub fn list_return( func_id: FunctionIdent, - args: &[Value], + args: &[ValueRef], builder: &mut FunctionBuilderExt, span: SourceSpan, _diagnostics: &DiagnosticsHandler, -) -> Vec { +) -> Vec { let call = builder.ins().exec(func_id, args, span); let results = builder.inst_results(call); assert_eq!(results.len(), 2, "List return strategy expects 2 results: length and pointer"); @@ -109,11 +109,11 @@ pub fn list_return( /// The Miden ABI function returns felts on the stack and we want to return via a pointer argument pub fn return_via_pointer( func_id: FunctionIdent, - args: &[Value], + args: &[ValueRef], builder: &mut FunctionBuilderExt, span: SourceSpan, _diagnostics: &DiagnosticsHandler, -) -> Vec { +) -> Vec { // Omit the last argument (pointer) let args_wo_pointer = &args[0..args.len() - 1]; let call = builder.ins().exec(func_id, args_wo_pointer, span); diff --git a/frontend-wasm2/src/module/build_ir.rs b/frontend-wasm2/src/module/build_ir.rs index dd9ae485..3e872bec 100644 --- a/frontend-wasm2/src/module/build_ir.rs +++ b/frontend-wasm2/src/module/build_ir.rs @@ -74,17 +74,13 @@ pub fn translate_module_as_component( .create::(Default::default())(ns, name, ver) .unwrap(); let mut cb = ComponentBuilder::new(component_ref); - let mut module_ref = - cb.define_module(Ident::from(parsed_module.module.name().as_str())).unwrap(); + let mut module_ref = cb + .define_module(Ident::from(parsed_module.module.name().as_str())) + .unwrap() + .borrow_mut(); + let module = module_ref.as_mut().downcast_mut::().unwrap(); - build_ir_module( - &mut parsed_module, - &module_types, - &mut module_state, - config, - context, - module_ref, - )?; + build_ir_module(&mut parsed_module, &module_types, &mut module_state, config, context, module)?; // TODO: translate core module imports (create empty Components?) // @@ -118,15 +114,14 @@ pub fn build_ir_module( module_state: &mut ModuleTranslationState, _config: &WasmTranslationConfig, context: Rc, - module_ref: ModuleRef, + module_ref: &mut Module, ) -> WasmResult<()> { let memory_size = parsed_module .module .memories .get(MemoryIndex::from_u32(0)) .map(|mem| mem.minimum as u32); - let mut module_builder = - ModuleBuilder::new(module_ref.borrow_mut().as_mut().downcast_mut::().unwrap()); + let mut module_builder = ModuleBuilder::new(module_ref); // if let Some(memory_size) = memory_size { // module_builder.with_reserved_memory_pages(memory_size); // } @@ -169,17 +164,17 @@ pub fn build_ir_module( Linkage::Internal }; let sig = ir_func_sig(&ir_func_type, CallConv::SystemV, linkage); - let function_ref = - module_builder.define_function(Ident::from(func_name.as_str()), sig).unwrap(); - let mut module_func_builder = FunctionBuilder::new( - function_ref.borrow_mut().as_mut().downcast_mut::().unwrap(), - ); + let mut function_ref = module_builder + .define_function(Ident::from(func_name.as_str()), sig) + .unwrap() + .borrow_mut(); + let func = function_ref.as_mut().downcast_mut::().unwrap(); // let mut module_func_builder = module_builder.function(func_name.as_str(), sig.clone())?; let FunctionBodyData { validator, body } = body_data; let mut func_validator = validator.into_validator(Default::default()); func_translator.translate_body( &body, - &mut module_func_builder, + func, module_state, parsed_module, module_types, diff --git a/frontend-wasm2/src/module/func_translation_state.rs b/frontend-wasm2/src/module/func_translation_state.rs index a4b39c9e..1244d026 100644 --- a/frontend-wasm2/src/module/func_translation_state.rs +++ b/frontend-wasm2/src/module/func_translation_state.rs @@ -5,7 +5,9 @@ //! //! Based on Cranelift's Wasm -> CLIF translator v11.0.0 -use midenc_hir::{diagnostics::SourceSpan, Block, Inst, InstBuilder, Signature, Value}; +use midenc_dialect_hir::InstBuilder; +use midenc_hir::{diagnostics::SourceSpan, Block, Inst}; +use midenc_hir2::{BlockRef, Signature, ValueRef}; use midenc_hir_type::Type; use super::function_builder_ext::FunctionBuilderExt; @@ -26,7 +28,7 @@ pub enum ElseData { branch_inst: Inst, /// The placeholder block we're replacing. - placeholder: Block, + placeholder: BlockRef, }, /// We have already allocated an `else` block. @@ -37,23 +39,23 @@ pub enum ElseData { /// these cases, we pre-allocate the `else` block. WithElse { /// This is the `else` block. - else_block: Block, + else_block: BlockRef, }, } /// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following /// fields: /// -/// - `destination`: reference to the `Block` that will hold the code after the control block; +/// - `destination`: reference to the `BlockRef` that will hold the code after the control block; /// - `num_return_values`: number of values returned by the control block; /// - `original_stack_size`: size of the value stack at the beginning of the control block. /// -/// The `loop` frame has a `header` field that references the `Block` that contains the beginning +/// The `loop` frame has a `header` field that references the `BlockRef` that contains the beginning /// of the body of the loop. #[derive(Debug)] pub enum ControlStackFrame { If { - destination: Block, + destination: BlockRef, else_data: ElseData, num_param_values: usize, num_return_values: usize, @@ -73,15 +75,15 @@ pub enum ControlStackFrame { // `state.reachable` when we hit the `end` in the `if .. else .. end`. }, Block { - destination: Block, + destination: BlockRef, num_param_values: usize, num_return_values: usize, original_stack_size: usize, exit_is_branched_to: bool, }, Loop { - destination: Block, - header: Block, + destination: BlockRef, + header: BlockRef, num_param_values: usize, num_return_values: usize, original_stack_size: usize, @@ -118,7 +120,7 @@ impl ControlStackFrame { } } - pub fn following_code(&self) -> Block { + pub fn following_code(&self) -> BlockRef { match *self { Self::If { destination, .. } | Self::Block { destination, .. } @@ -126,7 +128,7 @@ impl ControlStackFrame { } } - pub fn br_destination(&self) -> Block { + pub fn br_destination(&self) -> BlockRef { match *self { Self::If { destination, .. } | Self::Block { destination, .. } => destination, Self::Loop { header, .. } => header, @@ -189,14 +191,14 @@ impl ControlStackFrame { /// Pop values from the value stack so that it is left at the /// input-parameters to an else-block. - pub fn truncate_value_stack_to_else_params(&self, stack: &mut Vec) { + pub fn truncate_value_stack_to_else_params(&self, stack: &mut Vec) { debug_assert!(matches!(self, &ControlStackFrame::If { .. })); stack.truncate(self.original_stack_size()); } /// Pop values from the value stack so that it is left at the state it was /// before this control-flow frame. - pub fn truncate_value_stack_to_original_size(&self, stack: &mut Vec) { + pub fn truncate_value_stack_to_original_size(&self, stack: &mut Vec) { // The "If" frame pushes its parameters twice, so they're available to the else block // (see also `FuncTranslationState::push_if`). // Yet, the original_stack_size member accounts for them only once, so that the else @@ -223,7 +225,7 @@ impl ControlStackFrame { pub struct FuncTranslationState { /// A stack of values corresponding to the active values in the input wasm function at this /// point. - pub(crate) stack: Vec, + pub(crate) stack: Vec, /// A stack of active control flow operations at this point in the input wasm function. pub(crate) control_stack: Vec, /// Is the current translation state still reachable? This is false when translating operators @@ -251,23 +253,23 @@ impl FuncTranslationState { /// /// This resets the state to containing only a single block representing the whole function. /// The exit block is the last block in the function which will contain the return instruction. - pub(crate) fn initialize(&mut self, sig: &Signature, exit_block: Block) { + pub(crate) fn initialize(&mut self, sig: &Signature, exit_block: BlockRef) { self.clear(); self.push_block(exit_block, 0, sig.results().len()); } /// Push a value. - pub(crate) fn push1(&mut self, val: Value) { + pub(crate) fn push1(&mut self, val: ValueRef) { self.stack.push(val); } /// Push multiple values. - pub(crate) fn pushn(&mut self, vals: &[Value]) { + pub(crate) fn pushn(&mut self, vals: &[ValueRef]) { self.stack.extend_from_slice(vals); } /// Pop one value. - pub(crate) fn pop1(&mut self) -> Value { + pub(crate) fn pop1(&mut self) -> ValueRef { self.stack.pop().expect("attempted to pop a value from an empty stack") } @@ -277,13 +279,14 @@ impl FuncTranslationState { ty: Type, builder: &mut FunctionBuilderExt, span: SourceSpan, - ) -> Value { - let val = self.stack.pop().expect("attempted to pop a value from an empty stack"); - if builder.data_flow_graph().value_type(val) != &ty { - builder.ins().cast(val, ty, span) - } else { - val - } + ) -> ValueRef { + todo!() + // let val = self.stack.pop().expect("attempted to pop a value from an empty stack"); + // if builder.data_flow_graph().value_type(val) != &ty { + // builder.ins().cast(val, ty, span) + // } else { + // val + // } } /// Pop one value and bitcast it to the specified type. @@ -292,22 +295,23 @@ impl FuncTranslationState { ty: Type, builder: &mut FunctionBuilderExt, span: SourceSpan, - ) -> Value { - let val = self.stack.pop().expect("attempted to pop a value from an empty stack"); - if builder.data_flow_graph().value_type(val) != &ty { - builder.ins().bitcast(val, ty, span) - } else { - val - } + ) -> ValueRef { + todo!() + // let val = self.stack.pop().expect("attempted to pop a value from an empty stack"); + // if builder.data_flow_graph().value_type(val) != &ty { + // builder.ins().bitcast(val, ty, span) + // } else { + // val + // } } /// Peek at the top of the stack without popping it. - pub(crate) fn peek1(&self) -> Value { + pub(crate) fn peek1(&self) -> ValueRef { *self.stack.last().expect("attempted to peek at a value on an empty stack") } /// Pop two values. Return them in the order they were pushed. - pub(crate) fn pop2(&mut self) -> (Value, Value) { + pub(crate) fn pop2(&mut self) -> (ValueRef, ValueRef) { let v2 = self.stack.pop().unwrap(); let v1 = self.stack.pop().unwrap(); (v1, v2) @@ -319,20 +323,21 @@ impl FuncTranslationState { ty: Type, builder: &mut FunctionBuilderExt, span: SourceSpan, - ) -> (Value, Value) { - let v2 = self.stack.pop().unwrap(); - let v1 = self.stack.pop().unwrap(); - let v1 = if builder.data_flow_graph().value_type(v1) != &ty { - builder.ins().cast(v1, ty.clone(), span) - } else { - v1 - }; - let v2 = if builder.data_flow_graph().value_type(v2) != &ty { - builder.ins().cast(v2, ty, span) - } else { - v2 - }; - (v1, v2) + ) -> (ValueRef, ValueRef) { + todo!() + // let v2 = self.stack.pop().unwrap(); + // let v1 = self.stack.pop().unwrap(); + // let v1 = if builder.data_flow_graph().value_type(v1) != &ty { + // builder.ins().cast(v1, ty.clone(), span) + // } else { + // v1 + // }; + // let v2 = if builder.data_flow_graph().value_type(v2) != &ty { + // builder.ins().cast(v2, ty, span) + // } else { + // v2 + // }; + // (v1, v2) } /// Pop two values. Bitcast them to the specified type. Return them in the order they were @@ -342,24 +347,25 @@ impl FuncTranslationState { ty: Type, builder: &mut FunctionBuilderExt, span: SourceSpan, - ) -> (Value, Value) { - let v2 = self.stack.pop().unwrap(); - let v1 = self.stack.pop().unwrap(); - let v1 = if builder.data_flow_graph().value_type(v1) != &ty { - builder.ins().bitcast(v1, ty.clone(), span) - } else { - v1 - }; - let v2 = if builder.data_flow_graph().value_type(v2) != &ty { - builder.ins().bitcast(v2, ty, span) - } else { - v2 - }; - (v1, v2) + ) -> (ValueRef, ValueRef) { + todo!() + // let v2 = self.stack.pop().unwrap(); + // let v1 = self.stack.pop().unwrap(); + // let v1 = if builder.data_flow_graph().value_type(v1) != &ty { + // builder.ins().bitcast(v1, ty.clone(), span) + // } else { + // v1 + // }; + // let v2 = if builder.data_flow_graph().value_type(v2) != &ty { + // builder.ins().bitcast(v2, ty, span) + // } else { + // v2 + // }; + // (v1, v2) } /// Pop three values. Return them in the order they were pushed. - pub(crate) fn pop3(&mut self) -> (Value, Value, Value) { + pub(crate) fn pop3(&mut self) -> (ValueRef, ValueRef, ValueRef) { let v3 = self.stack.pop().unwrap(); let v2 = self.stack.pop().unwrap(); let v1 = self.stack.pop().unwrap(); @@ -388,13 +394,13 @@ impl FuncTranslationState { } /// Peek at the top `n` values on the stack in the order they were pushed. - pub(crate) fn peekn(&self, n: usize) -> &[Value] { + pub(crate) fn peekn(&self, n: usize) -> &[ValueRef] { self.ensure_length_is_at_least(n); &self.stack[self.stack.len() - n..] } /// Peek at the top `n` values on the stack in the order they were pushed. - pub(crate) fn peekn_mut(&mut self, n: usize) -> &mut [Value] { + pub(crate) fn peekn_mut(&mut self, n: usize) -> &mut [ValueRef] { self.ensure_length_is_at_least(n); let len = self.stack.len(); &mut self.stack[len - n..] @@ -403,7 +409,7 @@ impl FuncTranslationState { /// Push a block on the control stack. pub(crate) fn push_block( &mut self, - following_code: Block, + following_code: BlockRef, num_param_types: usize, num_result_types: usize, ) { @@ -420,8 +426,8 @@ impl FuncTranslationState { /// Push a loop on the control stack. pub(crate) fn push_loop( &mut self, - header: Block, - following_code: Block, + header: BlockRef, + following_code: BlockRef, num_param_types: usize, num_result_types: usize, ) { @@ -438,7 +444,7 @@ impl FuncTranslationState { /// Push an if on the control stack. pub(crate) fn push_if( &mut self, - destination: Block, + destination: BlockRef, else_data: ElseData, num_param_types: usize, num_result_types: usize, diff --git a/frontend-wasm2/src/module/func_translator.rs b/frontend-wasm2/src/module/func_translator.rs index 1f9177f3..b067f28f 100644 --- a/frontend-wasm2/src/module/func_translator.rs +++ b/frontend-wasm2/src/module/func_translator.rs @@ -6,11 +6,15 @@ //! //! Based on Cranelift's Wasm -> CLIF translator v11.0.0 +use std::{cell::RefCell, rc::Rc}; + +use midenc_dialect_hir::{FunctionBuilder, InstBuilder}; use midenc_hir::{ cranelift_entity::EntityRef, diagnostics::{DiagnosticsHandler, IntoDiagnostic, SourceManagerExt, SourceSpan}, - Block, InstBuilder, ModuleFunctionBuilder, + Block, ModuleFunctionBuilder, }; +use midenc_hir2::{dialects::builtin::Function, BlockRef}; use midenc_session::Session; use wasmparser::{FuncValidator, FunctionBody, WasmModuleResources}; @@ -34,7 +38,7 @@ use crate::{ /// by a `FuncEnvironment` object. A single translator instance can be reused to translate multiple /// functions which will reduce heap allocation traffic. pub struct FuncTranslator { - func_ctx: FunctionBuilderContext, + func_ctx: Rc>, state: FuncTranslationState, } @@ -42,7 +46,7 @@ impl FuncTranslator { /// Create a new translator. pub fn new() -> Self { Self { - func_ctx: FunctionBuilderContext::new(), + func_ctx: Rc::new(RefCell::new(FunctionBuilderContext::new())), state: FuncTranslationState::new(), } } @@ -52,7 +56,8 @@ impl FuncTranslator { pub fn translate_body( &mut self, body: &FunctionBody<'_>, - mod_func_builder: &mut ModuleFunctionBuilder, + // mod_func_builder: &mut FunctionBuilder<'_>, + func: &mut Function, module_state: &mut ModuleTranslationState, module: &ParsedModule<'_>, mod_types: &ModuleTypes, @@ -60,7 +65,7 @@ impl FuncTranslator { session: &Session, func_validator: &mut FuncValidator, ) -> WasmResult<()> { - let mut builder = FunctionBuilderExt::new(mod_func_builder, &mut self.func_ctx); + let mut builder = FunctionBuilderExt::new(func, self.func_ctx.clone()); let entry_block = builder.current_block(); builder.seal_block(entry_block); // Declare all predecessors known. @@ -103,7 +108,7 @@ impl FuncTranslator { /// Declare local variables for the signature parameters that correspond to WebAssembly locals. /// /// Return the number of local variables declared. -fn declare_parameters(builder: &mut FunctionBuilderExt, entry_block: Block) -> usize { +fn declare_parameters(builder: &mut FunctionBuilderExt, entry_block: BlockRef) -> usize { let sig_len = builder.signature().params().len(); let mut next_local = 0; for i in 0..sig_len { @@ -168,9 +173,9 @@ fn declare_locals( /// This assumes that the local variable declarations have already been parsed and function /// arguments and locals are declared in the builder. #[allow(clippy::too_many_arguments)] -fn parse_function_body( +fn parse_function_body<'a: 'b, 'b>( reader: &mut wasmparser::OperatorsReader<'_>, - builder: &mut FunctionBuilderExt, + builder: &'a mut FunctionBuilderExt<'b>, state: &mut FuncTranslationState, module_state: &mut ModuleTranslationState, module: &ParsedModule<'_>, @@ -204,14 +209,14 @@ fn parse_function_body( } else { log::debug!( "failed to locate span for instruction at offset {offset} in function {}", - builder.id() + builder.name() ); } } } else { log::debug!( "failed to locate span for instruction at offset {offset} in function {}", - builder.id() + builder.name() ); } diff --git a/frontend-wasm2/src/module/function_builder_ext.rs b/frontend-wasm2/src/module/function_builder_ext.rs index f1df217e..03c6ba9f 100644 --- a/frontend-wasm2/src/module/function_builder_ext.rs +++ b/frontend-wasm2/src/module/function_builder_ext.rs @@ -1,8 +1,14 @@ +use std::{cell::RefCell, rc::Rc}; + +use midenc_dialect_hir::{FunctionBuilder, InstBuilderBase}; use midenc_hir::{ cranelift_entity::{EntitySet, SecondaryMap}, diagnostics::SourceSpan, - Block, Br, CondBr, DataFlowGraph, InsertionPoint, Inst, InstBuilderBase, Instruction, - ModuleFunctionBuilder, ProgramPoint, Switch, Value, + DefaultInstBuilder, +}; +use midenc_hir2::{ + dialects::builtin::Function, BlockArgumentRef, BlockRef, FxHashMap, Ident, Listener, Op, + OpBuilder, OperationRef, ProgramPoint, RegionRef, Signature, ValueRef, }; use midenc_hir_type::Type; @@ -11,7 +17,7 @@ use crate::ssa::{SSABuilder, SideEffects, Variable}; /// Tracking variables and blocks for SSA construction. pub struct FunctionBuilderContext { ssa: SSABuilder, - status: SecondaryMap, + status: FxHashMap, types: SecondaryMap, } @@ -19,7 +25,7 @@ impl FunctionBuilderContext { pub fn new() -> Self { Self { ssa: SSABuilder::default(), - status: SecondaryMap::new(), + status: Default::default(), types: SecondaryMap::with_default(Type::Unknown), } } @@ -46,55 +52,199 @@ enum BlockStatus { Filled, } +pub struct SSABuilderListener { + builder: Rc>, +} + +impl Listener for SSABuilderListener { + fn kind(&self) -> midenc_hir2::ListenerType { + todo!() + } + + fn notify_operation_inserted(&self, op: OperationRef, prev: ProgramPoint) { + // TODO: implement + + // let builder = self.builder.borrow_mut(); + // // We only insert the Block in the layout when an instruction is added to it + // builder.ensure_inserted_block(); + // let opcode = data.opcode(); + // // let inst = self.builder.data_flow_graph_mut().insert_inst(self.ip, data, ty, span); + // + // match self.builder.inner.data_flow_graph().insts[inst].data.inner() { + // Instruction::Br(Br { successor, .. }) => { + // // If the user has supplied jump arguments we must adapt the arguments of + // // the destination block + // builder.func_ctx.ssa.declare_block_predecessor(successor.destination, inst); + // } + // + // Instruction::CondBr(CondBr { + // then_dest, + // else_dest, + // .. + // }) => { + // builder.func_ctx.ssa.declare_block_predecessor(then_dest.destination, inst); + // if then_dest.destination != else_dest.destination { + // builder.func_ctx.ssa.declare_block_predecessor(else_dest.destination, inst); + // } + // } + // Instruction::Switch(Switch { + // op: _, + // arg: _, + // ref arms, + // default: default_successor, + // }) => { + // // Unlike all other jumps/branches, arms are + // // capable of having the same successor appear + // // multiple times, so we must deduplicate. + // let mut unique = EntitySet::::new(); + // let blocks = arms + // .iter() + // .map(|arm| arm.successor.destination) + // .chain([default_successor.destination]); + // for block in blocks { + // if !unique.insert(block) { + // continue; + // } + // builder.func_ctx.ssa.declare_block_predecessor(block, inst); + // } + // } + // inst => debug_assert!(!inst.opcode().is_branch()), + // } + // + // if opcode.is_terminator() { + // builder.fill_current_block() + // } + } + + fn notify_block_inserted( + &self, + block: BlockRef, + prev: Option, + ip: Option, + ) { + } +} + +// TODO: implement in SSABuilderListener + +// This implementation is richer than `InsertBuilder` because we use the data of the +// instruction being inserted to add related info to the DFG and the SSA building system, +// and perform debug sanity checks. +// fn build(self, data: Instruction, ty: Type, span: SourceSpan) -> (Inst, &'a mut DataFlowGraph) { +// // We only insert the Block in the layout when an instruction is added to it +// self.builder.ensure_inserted_block(); +// let opcode = data.opcode(); +// let inst = self.builder.data_flow_graph_mut().insert_inst(self.ip, data, ty, span); +// +// match self.builder.inner.data_flow_graph().insts[inst].data.inner() { +// Instruction::Br(Br { successor, .. }) => { +// // If the user has supplied jump arguments we must adapt the arguments of +// // the destination block +// self.builder.func_ctx.ssa.declare_block_predecessor(successor.destination, inst); +// } +// +// Instruction::CondBr(CondBr { +// then_dest, +// else_dest, +// .. +// }) => { +// self.builder.func_ctx.ssa.declare_block_predecessor(then_dest.destination, inst); +// if then_dest.destination != else_dest.destination { +// self.builder +// .func_ctx +// .ssa +// .declare_block_predecessor(else_dest.destination, inst); +// } +// } +// Instruction::Switch(Switch { +// op: _, +// arg: _, +// ref arms, +// default: default_successor, +// }) => { +// // Unlike all other jumps/branches, arms are +// // capable of having the same successor appear +// // multiple times, so we must deduplicate. +// let mut unique = EntitySet::::new(); +// let blocks = arms +// .iter() +// .map(|arm| arm.successor.destination) +// .chain([default_successor.destination]); +// for block in blocks { +// if !unique.insert(block) { +// continue; +// } +// self.builder.func_ctx.ssa.declare_block_predecessor(block, inst); +// } +// } +// inst => debug_assert!(!inst.opcode().is_branch()), +// } +// +// if opcode.is_terminator() { +// self.builder.fill_current_block() +// } +// (inst, self.builder.data_flow_graph_mut()) +// } + /// A wrapper around Miden's `FunctionBuilder` and `SSABuilder` which provides /// additional API for dealing with variables and SSA construction. -pub struct FunctionBuilderExt<'a, 'b, 'c: 'b> { - inner: &'b mut ModuleFunctionBuilder<'c>, - func_ctx: &'a mut FunctionBuilderContext, +pub struct FunctionBuilderExt<'c, L: Listener = SSABuilderListener> { + inner: FunctionBuilder<'c, L>, + func_ctx: Rc>, } -impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { - pub fn new( - inner: &'b mut ModuleFunctionBuilder<'c>, - func_ctx: &'a mut FunctionBuilderContext, - ) -> Self { - debug_assert!(func_ctx.is_empty()); +impl<'c> FunctionBuilderExt<'c> { + pub fn new(func: &'c mut Function, func_ctx: Rc>) -> Self { + debug_assert!(func_ctx.borrow().is_empty()); + + let context = func.as_operation().context_rc(); + let ssa_builder_listener = SSABuilderListener { + builder: func_ctx.clone(), + }; + let op_builder = OpBuilder::new(context).with_listener(ssa_builder_listener); + let inner = FunctionBuilder::new(func, op_builder); Self { inner, func_ctx } } - pub fn data_flow_graph(&self) -> &DataFlowGraph { - self.inner.data_flow_graph() - } + // pub fn data_flow_graph(&self) -> &DataFlowGraph { + // todo!() + // // self.inner.data_flow_graph() + // } - pub fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph { - self.inner.data_flow_graph_mut() - } + // pub fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph { + // // self.inner.data_flow_graph_mut() + // todo!() + // } - pub fn id(&self) -> midenc_hir::FunctionIdent { - self.inner.id() + pub fn name(&self) -> &Ident { + self.inner.func.name() } - pub fn signature(&self) -> &midenc_hir::Signature { - self.inner.signature() + pub fn signature(&self) -> &Signature { + self.inner.func.signature() } - pub fn ins<'short>(&'short mut self) -> FuncInstBuilderExt<'short, 'a, 'b, 'c> { - let block = self.inner.current_block(); - FuncInstBuilderExt::new(self, block) + pub fn ins<'short>(&'short mut self) -> FuncInstBuilderExt<'short, 'c> { + // pub fn ins<'short>(&'short mut self) -> DefaultInstBuilder<'short, L> { + // let block = self.inner.current_block(); + // self.inner.ins() + FuncInstBuilderExt::new(self) } + // TODO: remove #[inline] - pub fn current_block(&self) -> Block { + pub fn current_block(&self) -> BlockRef { self.inner.current_block() } - pub fn inst_results(&self, inst: Inst) -> &[Value] { - self.inner.inst_results(inst) - } + // pub fn inst_results(&self, inst: OperationRef) -> &[ValueRef] { + // inst.borrow().results() + // } - pub fn create_block(&mut self) -> Block { + pub fn create_block(&mut self) -> BlockRef { let block = self.inner.create_block(); - self.func_ctx.ssa.declare_block(block); + todo!("declare block"); + // self.func_ctx.borrow_mut().ssa.declare_block(block); block } @@ -103,7 +253,7 @@ impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { &mut self, params: impl IntoIterator, span: SourceSpan, - ) -> Block { + ) -> BlockRef { let block = self.create_block(); for ty in params { self.inner.append_block_param(block, ty, span); @@ -114,18 +264,19 @@ impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { /// Append parameters to the given `Block` corresponding to the function /// return values. This can be used to set up the block parameters for a /// function exit block. - pub fn append_block_params_for_function_returns(&mut self, block: Block) { - // These parameters count as "user" parameters here because they aren't - // inserted by the SSABuilder. - debug_assert!( - self.is_pristine(block), - "You can't add block parameters after adding any instruction" - ); - - #[allow(clippy::unnecessary_to_owned)] - for argtyp in self.signature().results().to_vec() { - self.inner.append_block_param(block, argtyp.ty.clone(), SourceSpan::default()); - } + pub fn append_block_params_for_function_returns(&mut self, block: BlockRef) { + todo!() + // // These parameters count as "user" parameters here because they aren't + // // inserted by the SSABuilder. + // debug_assert!( + // self.is_pristine(block), + // "You can't add block parameters after adding any instruction" + // ); + // + // #[allow(clippy::unnecessary_to_owned)] + // for argtyp in self.signature().results().to_vec() { + // self.inner.append_block_param(block, argtyp.ty.clone(), SourceSpan::default()); + // } } /// After the call to this function, new instructions will be inserted into the designated @@ -135,27 +286,29 @@ impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { /// When inserting the terminator instruction (which doesn't have a fallthrough to its immediate /// successor), the block will be declared filled and it will not be possible to append /// instructions to it. - pub fn switch_to_block(&mut self, block: Block) { - // First we check that the previous block has been filled. - debug_assert!( - self.is_unreachable() - || self.is_pristine(self.inner.current_block()) - || self.is_filled(self.inner.current_block()), - "you have to fill your block before switching" - ); - // We cannot switch to a filled block - debug_assert!( - !self.is_filled(block), - "you cannot switch to a block which is already filled" - ); - // Then we change the cursor position. - self.inner.switch_to_block(block); + pub fn switch_to_block(&mut self, block: BlockRef) { + todo!() + // // First we check that the previous block has been filled. + // debug_assert!( + // self.is_unreachable() + // || self.is_pristine(self.inner.current_block()) + // || self.is_filled(self.inner.current_block()), + // "you have to fill your block before switching" + // ); + // // We cannot switch to a filled block + // debug_assert!( + // !self.is_filled(block), + // "you cannot switch to a block which is already filled" + // ); + // // Then we change the cursor position. + // self.inner.switch_to_block(block); } /// Retrieves all the parameters for a `Block` currently inferred from the jump instructions /// inserted that target it and the SSA construction. - pub fn block_params(&self, block: Block) -> &[Value] { - self.inner.block_params(block) + pub fn block_params(&self, block: BlockRef) -> &[BlockArgumentRef] { + // block.borrow().arguments() + todo!() } /// Declares that all the predecessors of this block are known. @@ -163,35 +316,43 @@ impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { /// Function to call with `block` as soon as the last branch instruction to `block` has been /// created. Forgetting to call this method on every block will cause inconsistencies in the /// produced functions. - pub fn seal_block(&mut self, block: Block) { - let side_effects = self.func_ctx.ssa.seal_block(block, self.inner.data_flow_graph_mut()); - self.handle_ssa_side_effects(side_effects); + pub fn seal_block(&mut self, block: BlockRef) { + todo!(); + // let side_effects = self + // .func_ctx + // .borrow_mut() + // .ssa + // .seal_block(block, self.inner.data_flow_graph_mut()); + // self.handle_ssa_side_effects(side_effects); } /// A Block is 'filled' when a terminator instruction is present. fn fill_current_block(&mut self) { - self.func_ctx.status[self.inner.current_block()] = BlockStatus::Filled; + todo!() + // self.func_ctx.borrow_mut().status[&self.inner.current_block()] = BlockStatus::Filled; } fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) { - for modified_block in side_effects.instructions_added_to_blocks { - if self.is_pristine(modified_block) { - self.func_ctx.status[modified_block] = BlockStatus::Partial; - } - } + todo!() + // for modified_block in side_effects.instructions_added_to_blocks { + // if self.is_pristine(modified_block) { + // self.func_ctx.status[modified_block] = BlockStatus::Partial; + // } + // } } /// Make sure that the current block is inserted in the layout. pub fn ensure_inserted_block(&mut self) { - let block = self.inner.current_block(); - if self.is_pristine(block) { - self.func_ctx.status[block] = BlockStatus::Partial; - } else { - debug_assert!( - !self.is_filled(block), - "you cannot add an instruction to a block already filled" - ); - } + todo!() + // let block = self.inner.current_block(); + // if self.is_pristine(block) { + // self.func_ctx.status[block] = BlockStatus::Partial; + // } else { + // debug_assert!( + // !self.is_filled(block), + // "you cannot add an instruction to a block already filled" + // ); + // } } /// Declare that translation of the current function is complete. @@ -199,44 +360,47 @@ impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { /// This resets the state of the `FunctionBuilderContext` in preparation to /// be used for another function. pub fn finalize(self) { - // Check that all the `Block`s are filled and sealed. - #[cfg(debug_assertions)] - { - for block in self.func_ctx.status.keys() { - if !self.is_pristine(block) { - assert!( - self.func_ctx.ssa.is_sealed(block), - "FunctionBuilderExt finalized, but block {} is not sealed", - block, - ); - assert!( - self.is_filled(block), - "FunctionBuilderExt finalized, but block {} is not filled", - block, - ); - } - } - } - - // Clear the state (but preserve the allocated buffers) in preparation - // for translation another function. - self.func_ctx.clear(); + todo!() + // // Check that all the `Block`s are filled and sealed. + // #[cfg(debug_assertions)] + // { + // for block in self.func_ctx.status.keys() { + // if !self.is_pristine(block) { + // assert!( + // self.func_ctx.ssa.is_sealed(block), + // "FunctionBuilderExt finalized, but block {} is not sealed", + // block, + // ); + // assert!( + // self.is_filled(block), + // "FunctionBuilderExt finalized, but block {} is not filled", + // block, + // ); + // } + // } + // } + // + // // Clear the state (but preserve the allocated buffers) in preparation + // // for translation another function. + // self.func_ctx.clear(); } #[inline] pub fn variable_type(&self, var: Variable) -> &Type { - &self.func_ctx.types[var] + todo!() + // &self.func_ctx.types[var] } /// Declares the type of a variable, so that it can be used later (by calling /// [`FunctionBuilderExt::use_var`]). This function will return an error if the variable /// has been previously declared. pub fn try_declare_var(&mut self, var: Variable, ty: Type) -> Result<(), DeclareVariableError> { - if self.func_ctx.types[var] != Type::Unknown { - return Err(DeclareVariableError::DeclaredMultipleTimes(var)); - } - self.func_ctx.types[var] = ty; - Ok(()) + todo!() + // if self.func_ctx.types[var] != Type::Unknown { + // return Err(DeclareVariableError::DeclaredMultipleTimes(var)); + // } + // self.func_ctx.types[var] = ty; + // Ok(()) } /// In order to use a variable (by calling [`FunctionBuilderExt::use_var`]), you need @@ -248,38 +412,39 @@ impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { /// Returns the Miden IR necessary to use a previously defined user /// variable, returning an error if this is not possible. - pub fn try_use_var(&mut self, var: Variable) -> Result { - // Assert that we're about to add instructions to this block using the definition of the - // given variable. ssa.use_var is the only part of this crate which can add block parameters - // behind the caller's back. If we disallow calling append_block_param as soon as use_var is - // called, then we enforce a strict separation between user parameters and SSA parameters. - self.ensure_inserted_block(); - - let (val, side_effects) = { - let ty = self - .func_ctx - .types - .get(var) - .cloned() - .ok_or(UseVariableError::UsedBeforeDeclared(var))?; - debug_assert_ne!( - ty, - Type::Unknown, - "variable {:?} is used but its type has not been declared", - var - ); - let current_block = self.inner.current_block(); - self.func_ctx - .ssa - .use_var(self.inner.data_flow_graph_mut(), var, ty, current_block) - }; - self.handle_ssa_side_effects(side_effects); - Ok(val) + pub fn try_use_var(&mut self, var: Variable) -> Result { + todo!() + // // Assert that we're about to add instructions to this block using the definition of the + // // given variable. ssa.use_var is the only part of this crate which can add block parameters + // // behind the caller's back. If we disallow calling append_block_param as soon as use_var is + // // called, then we enforce a strict separation between user parameters and SSA parameters. + // self.ensure_inserted_block(); + // + // let (val, side_effects) = { + // let ty = self + // .func_ctx + // .types + // .get(var) + // .cloned() + // .ok_or(UseVariableError::UsedBeforeDeclared(var))?; + // debug_assert_ne!( + // ty, + // Type::Unknown, + // "variable {:?} is used but its type has not been declared", + // var + // ); + // let current_block = self.inner.current_block(); + // self.func_ctx + // .ssa + // .use_var(self.inner.data_flow_graph_mut(), var, ty, current_block) + // }; + // self.handle_ssa_side_effects(side_effects); + // Ok(val) } /// Returns the Miden IR value corresponding to the utilization at the current program /// position of a previously defined user variable. - pub fn use_var(&mut self, var: Variable) -> Value { + pub fn use_var(&mut self, var: Variable) -> ValueRef { self.try_use_var(var).unwrap_or_else(|_| { panic!("variable {:?} is used but its type has not been declared", var) }) @@ -288,49 +453,51 @@ impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { /// Registers a new definition of a user variable. This function will return /// an error if the value supplied does not match the type the variable was /// declared to have. - pub fn try_def_var(&mut self, var: Variable, val: Value) -> Result<(), DefVariableError> { - let var_ty = self - .func_ctx - .types - .get(var) - .ok_or(DefVariableError::DefinedBeforeDeclared(var))?; - if var_ty != self.data_flow_graph().value_type(val) { - return Err(DefVariableError::TypeMismatch(var, val)); - } - - self.func_ctx.ssa.def_var(var, val, self.inner.current_block()); - Ok(()) + pub fn try_def_var(&mut self, var: Variable, val: ValueRef) -> Result<(), DefVariableError> { + todo!() + // let var_ty = self + // .func_ctx + // .types + // .get(var) + // .ok_or(DefVariableError::DefinedBeforeDeclared(var))?; + // if var_ty != self.data_flow_graph().value_type(val) { + // return Err(DefVariableError::TypeMismatch(var, val)); + // } + // + // self.func_ctx.ssa.def_var(var, val, self.inner.current_block()); + // Ok(()) } /// Register a new definition of a user variable. The type of the value must be /// the same as the type registered for the variable. - pub fn def_var(&mut self, var: Variable, val: Value) { - self.try_def_var(var, val).unwrap_or_else(|error| match error { - DefVariableError::TypeMismatch(var, val) => { - assert_eq!( - &self.func_ctx.types[var], - self.data_flow_graph().value_type(val), - "declared type of variable {:?} doesn't match type of value {}", - var, - val - ); - } - DefVariableError::DefinedBeforeDeclared(var) => { - panic!("variable {:?} is used but its type has not been declared", var); - } - }) + pub fn def_var(&mut self, var: Variable, val: ValueRef) { + todo!() + // self.try_def_var(var, val).unwrap_or_else(|error| match error { + // DefVariableError::TypeMismatch(var, val) => { + // assert_eq!( + // &self.func_ctx.types[var], + // self.data_flow_graph().value_type(val), + // "declared type of variable {:?} doesn't match type of value {}", + // var, + // val + // ); + // } + // DefVariableError::DefinedBeforeDeclared(var) => { + // panic!("variable {:?} is used but its type has not been declared", var); + // } + // }) } /// Returns `true` if and only if no instructions have been added since the last call to /// `switch_to_block`. - fn is_pristine(&self, block: Block) -> bool { - self.func_ctx.status[block] == BlockStatus::Empty + fn is_pristine(&self, block: &BlockRef) -> bool { + self.func_ctx.borrow().status[block] == BlockStatus::Empty } /// Returns `true` if and only if a terminator instruction has been inserted since the /// last call to `switch_to_block`. - fn is_filled(&self, block: Block) -> bool { - self.func_ctx.status[block] == BlockStatus::Filled + fn is_filled(&self, block: &BlockRef) -> bool { + self.func_ctx.borrow_mut().status[block] == BlockStatus::Filled } /// Returns `true` if and only if the current `Block` is sealed and has no predecessors @@ -338,53 +505,60 @@ impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { /// /// The entry block of a function is never unreachable. pub fn is_unreachable(&self) -> bool { - let is_entry = self.inner.current_block() == self.data_flow_graph().entry_block(); - !is_entry - && self.func_ctx.ssa.is_sealed(self.inner.current_block()) - && !self.func_ctx.ssa.has_any_predecessors(self.inner.current_block()) + todo!() + // let is_entry = self.inner.current_block() == self.data_flow_graph().entry_block(); + // !is_entry + // && self.func_ctx.ssa.is_sealed(self.inner.current_block()) + // && !self.func_ctx.ssa.has_any_predecessors(self.inner.current_block()) } /// Changes the destination of a jump instruction after creation. /// /// **Note:** You are responsible for maintaining the coherence with the arguments of /// other jump instructions. - pub fn change_jump_destination(&mut self, inst: Inst, old_block: Block, new_block: Block) { - self.func_ctx.ssa.remove_block_predecessor(old_block, inst); - match &mut *self.data_flow_graph_mut().insts[inst].data { - Instruction::Br(Br { - ref mut successor, .. - }) if successor.destination == old_block => { - successor.destination = new_block; - } - Instruction::CondBr(CondBr { - ref mut then_dest, - ref mut else_dest, - .. - }) => { - if then_dest.destination == old_block { - then_dest.destination = new_block; - } else if else_dest.destination == old_block { - else_dest.destination = new_block; - } - } - Instruction::Switch(Switch { - op: _, - arg: _, - ref mut arms, - ref mut default, - }) => { - for arm in arms.iter_mut() { - if arm.successor.destination == old_block { - arm.successor.destination = new_block; - } - } - if default.destination == old_block { - default.destination = new_block; - } - } - _ => panic!("{} must be a branch instruction", inst), - } - self.func_ctx.ssa.declare_block_predecessor(new_block, inst); + pub fn change_jump_destination( + &mut self, + inst: OperationRef, + old_block: BlockRef, + new_block: BlockRef, + ) { + todo!() + // self.func_ctx.ssa.remove_block_predecessor(old_block, inst); + // match &mut *self.data_flow_graph_mut().insts[inst].data { + // Instruction::Br(Br { + // ref mut successor, .. + // }) if successor.destination == old_block => { + // successor.destination = new_block; + // } + // Instruction::CondBr(CondBr { + // ref mut then_dest, + // ref mut else_dest, + // .. + // }) => { + // if then_dest.destination == old_block { + // then_dest.destination = new_block; + // } else if else_dest.destination == old_block { + // else_dest.destination = new_block; + // } + // } + // Instruction::Switch(Switch { + // op: _, + // arg: _, + // ref mut arms, + // ref mut default, + // }) => { + // for arm in arms.iter_mut() { + // if arm.successor.destination == old_block { + // arm.successor.destination = new_block; + // } + // } + // if default.destination == old_block { + // default.destination = new_block; + // } + // } + // _ => panic!("{} must be a branch instruction", inst), + // } + // self.func_ctx.ssa.declare_block_predecessor(new_block, inst); } } @@ -410,7 +584,7 @@ pub enum DefVariableError { `def_var` must be of the same type as the variable was declared to be of in \ `declare_var`." )] - TypeMismatch(Variable, Value), + TypeMismatch(Variable, ValueRef), #[error( "the value of variable {0} was defined (in call `def_val`) before it was declared (in \ call `declare_var`)" @@ -418,88 +592,30 @@ pub enum DefVariableError { DefinedBeforeDeclared(Variable), } -pub struct FuncInstBuilderExt<'a, 'b: 'a, 'c, 'd: 'c> { - builder: &'a mut FunctionBuilderExt<'b, 'c, 'd>, - ip: InsertionPoint, +pub struct FuncInstBuilderExt<'a, 'b> { + builder: &'b mut FunctionBuilderExt<'a>, } -impl<'a, 'b, 'c, 'd> FuncInstBuilderExt<'a, 'b, 'c, 'd> { - fn new(builder: &'a mut FunctionBuilderExt<'b, 'c, 'd>, block: Block) -> Self { - assert!(builder.data_flow_graph().is_block_linked(block)); - Self { - builder, - ip: InsertionPoint::after(ProgramPoint::Block(block)), - } +impl<'a, 'b> FuncInstBuilderExt<'a, 'b> { + fn new(builder: &'b mut FunctionBuilderExt<'a>) -> Self { + // assert!(builder.data_flow_graph().is_block_linked(block)); + Self { builder } } } -impl<'a> InstBuilderBase<'a> for FuncInstBuilderExt<'a, '_, '_, '_> { - fn data_flow_graph(&self) -> &DataFlowGraph { - self.builder.data_flow_graph() - } - - fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph { - self.builder.data_flow_graph_mut() - } - - fn insertion_point(&self) -> InsertionPoint { - self.ip - } - - // This implementation is richer than `InsertBuilder` because we use the data of the - // instruction being inserted to add related info to the DFG and the SSA building system, - // and perform debug sanity checks. - fn build(self, data: Instruction, ty: Type, span: SourceSpan) -> (Inst, &'a mut DataFlowGraph) { - // We only insert the Block in the layout when an instruction is added to it - self.builder.ensure_inserted_block(); - let opcode = data.opcode(); - let inst = self.builder.data_flow_graph_mut().insert_inst(self.ip, data, ty, span); - - match self.builder.inner.data_flow_graph().insts[inst].data.inner() { - Instruction::Br(Br { successor, .. }) => { - // If the user has supplied jump arguments we must adapt the arguments of - // the destination block - self.builder.func_ctx.ssa.declare_block_predecessor(successor.destination, inst); - } - - Instruction::CondBr(CondBr { - then_dest, - else_dest, - .. - }) => { - self.builder.func_ctx.ssa.declare_block_predecessor(then_dest.destination, inst); - if then_dest.destination != else_dest.destination { - self.builder - .func_ctx - .ssa - .declare_block_predecessor(else_dest.destination, inst); - } - } - Instruction::Switch(Switch { - op: _, - arg: _, - ref arms, - default: default_successor, - }) => { - // Unlike all other jumps/branches, arms are - // capable of having the same successor appear - // multiple times, so we must deduplicate. - let mut unique = EntitySet::::new(); - let blocks = arms - .iter() - .map(|arm| arm.successor.destination) - .chain([default_successor.destination]); - for block in blocks { - if !unique.insert(block) { - continue; - } - self.builder.func_ctx.ssa.declare_block_predecessor(block, inst); - } - } - inst => debug_assert!(!inst.opcode().is_branch()), - } - if opcode.is_terminator() { - self.builder.fill_current_block() - } - (inst, self.builder.data_flow_graph_mut()) +impl<'a> InstBuilderBase<'a> for FuncInstBuilderExt<'a, '_> { + type L = SSABuilderListener; + + fn builder(&self) -> &OpBuilder { + self.builder.inner.builder() // as &OpBuilder + } + + fn builder_mut(&mut self) -> &mut OpBuilder { + self.builder.inner.builder_mut() } + + // fn builder_parts( + // &mut self, + // ) -> (&mut midenc_hir2::dialects::builtin::Function, &mut OpBuilder) { + // (self.builder.inner.func, self.builder.inner.builder_mut()) + // } } diff --git a/frontend-wasm2/src/translation_utils.rs b/frontend-wasm2/src/translation_utils.rs index 452fb7df..64c74efb 100644 --- a/frontend-wasm2/src/translation_utils.rs +++ b/frontend-wasm2/src/translation_utils.rs @@ -4,6 +4,7 @@ use midenc_hir::{ diagnostics::{DiagnosticsHandler, SourceSpan}, AbiParam, CallConv, Felt, FieldElement, InstBuilder, Linkage, Signature, Value, }; +use midenc_hir2::ValueRef; use midenc_hir_type::{FunctionType, Type}; use crate::{ @@ -106,33 +107,34 @@ pub fn emit_zero( ty: &Type, builder: &mut FunctionBuilderExt, diagnostics: &DiagnosticsHandler, -) -> WasmResult { - Ok(match ty { - Type::I1 => builder.ins().i1(false, SourceSpan::default()), - Type::I8 => builder.ins().i8(0, SourceSpan::default()), - Type::I16 => builder.ins().i16(0, SourceSpan::default()), - Type::I32 => builder.ins().i32(0, SourceSpan::default()), - Type::I64 => builder.ins().i64(0, SourceSpan::default()), - Type::U8 => builder.ins().u8(0, SourceSpan::default()), - Type::U16 => builder.ins().u16(0, SourceSpan::default()), - Type::U32 => builder.ins().u32(0, SourceSpan::default()), - Type::U64 => builder.ins().u64(0, SourceSpan::default()), - Type::F64 => builder.ins().f64(0.0, SourceSpan::default()), - Type::Felt => builder.ins().felt(Felt::ZERO, SourceSpan::default()), - Type::I128 - | Type::U128 - | Type::U256 - | Type::Ptr(_) - | Type::NativePtr(..) - | Type::Struct(_) - | Type::Array(..) - | Type::List(_) - | Type::Unknown - | Type::Unit - | Type::Never => { - unsupported_diag!(diagnostics, "cannot emit zero value for type: {:?}", ty); - } - }) +) -> WasmResult { + todo!() + // Ok(match ty { + // Type::I1 => builder.ins().i1(false, SourceSpan::default()), + // Type::I8 => builder.ins().i8(0, SourceSpan::default()), + // Type::I16 => builder.ins().i16(0, SourceSpan::default()), + // Type::I32 => builder.ins().i32(0, SourceSpan::default()), + // Type::I64 => builder.ins().i64(0, SourceSpan::default()), + // Type::U8 => builder.ins().u8(0, SourceSpan::default()), + // Type::U16 => builder.ins().u16(0, SourceSpan::default()), + // Type::U32 => builder.ins().u32(0, SourceSpan::default()), + // Type::U64 => builder.ins().u64(0, SourceSpan::default()), + // Type::F64 => builder.ins().f64(0.0, SourceSpan::default()), + // Type::Felt => builder.ins().felt(Felt::ZERO, SourceSpan::default()), + // Type::I128 + // | Type::U128 + // | Type::U256 + // | Type::Ptr(_) + // | Type::NativePtr(..) + // | Type::Struct(_) + // | Type::Array(..) + // | Type::List(_) + // | Type::Unknown + // | Type::Unit + // | Type::Never => { + // unsupported_diag!(diagnostics, "cannot emit zero value for type: {:?}", ty); + // } + // }) } pub fn sig_from_func_type(