diff --git a/cranelift/filetests/verifier/type_check.cton b/cranelift/filetests/verifier/type_check.cton index b28f52930f..d8250389c4 100644 --- a/cranelift/filetests/verifier/type_check.cton +++ b/cranelift/filetests/verifier/type_check.cton @@ -95,3 +95,17 @@ function %jump_args2() { ebb1(v10: i64, v11: i16): return } + +function %bad_extend() { +ebb0: + v0 = iconst.i32 10 + v1 = uextend.i16 v0 ; error: input i32 must be smaller than output i16 + return +} + +function %bad_reduce() { +ebb0: + v0 = iconst.i32 10 + v1 = ireduce.i64 v0 ; error: input i32 must be larger than output i64 + return +} diff --git a/lib/cretonne/src/verifier/mod.rs b/lib/cretonne/src/verifier/mod.rs index 82ba075f39..d76a5efbba 100644 --- a/lib/cretonne/src/verifier/mod.rs +++ b/lib/cretonne/src/verifier/mod.rs @@ -49,8 +49,6 @@ //! //! - Stack slot loads and stores must be in-bounds. //! - Immediate constraints for certain opcodes, like `udiv_imm v3, 0`. -//! - Extend / truncate instructions have more type constraints: Source type can't be -//! larger / smaller than result type. //! - `Insertlane` and `extractlane` instructions have immediate lane numbers that must be in //! range for their polymorphic type. //! - Swizzle and shuffle instructions take a variable number of lane arguments. The number @@ -574,6 +572,7 @@ impl<'a> Verifier<'a> { self.typecheck_fixed_args(inst, ctrl_type)?; self.typecheck_variable_args(inst)?; self.typecheck_return(inst)?; + self.typecheck_special(inst, ctrl_type)?; Ok(()) } @@ -818,6 +817,57 @@ impl<'a> Verifier<'a> { Ok(()) } + // Check special-purpose type constraints that can't be expressed in the normal opcode + // constraints. + fn typecheck_special(&self, inst: Inst, ctrl_type: Type) -> Result { + match self.func.dfg[inst] { + ir::InstructionData::Unary { opcode, arg } => { + let arg_type = self.func.dfg.value_type(arg); + match opcode { + Opcode::Bextend | Opcode::Uextend | Opcode::Sextend | Opcode::Fpromote => { + if arg_type.lane_count() != ctrl_type.lane_count() { + return err!( + inst, + "input {} and output {} must have same number of lanes", + arg_type, + ctrl_type + ); + } + if arg_type.lane_bits() >= ctrl_type.lane_bits() { + return err!( + inst, + "input {} must be smaller than output {}", + arg_type, + ctrl_type + ); + } + } + Opcode::Breduce | Opcode::Ireduce | Opcode::Fdemote => { + if arg_type.lane_count() != ctrl_type.lane_count() { + return err!( + inst, + "input {} and output {} must have same number of lanes", + arg_type, + ctrl_type + ); + } + if arg_type.lane_bits() <= ctrl_type.lane_bits() { + return err!( + inst, + "input {} must be larger than output {}", + arg_type, + ctrl_type + ); + } + } + _ => {} + } + } + _ => {} + } + Ok(()) + } + fn cfg_integrity(&self, cfg: &ControlFlowGraph) -> Result { let mut expected_succs = BTreeSet::::new(); let mut got_succs = BTreeSet::::new(); diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index d96deafb0a..39a1d27253 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -486,33 +486,27 @@ pub fn translate_operator( /******************************* Unary Operators *************************************/ Operator::I32Clz => { let arg = state.pop1(); - let val = builder.ins().clz(arg); - state.push1(builder.ins().sextend(I32, val)); + state.push1(builder.ins().clz(arg)); } Operator::I64Clz => { let arg = state.pop1(); - let val = builder.ins().clz(arg); - state.push1(builder.ins().sextend(I64, val)); + state.push1(builder.ins().clz(arg)); } Operator::I32Ctz => { - let val = state.pop1(); - let short_res = builder.ins().ctz(val); - state.push1(builder.ins().sextend(I32, short_res)); + let arg = state.pop1(); + state.push1(builder.ins().ctz(arg)); } Operator::I64Ctz => { - let val = state.pop1(); - let short_res = builder.ins().ctz(val); - state.push1(builder.ins().sextend(I64, short_res)); + let arg = state.pop1(); + state.push1(builder.ins().ctz(arg)); } Operator::I32Popcnt => { let arg = state.pop1(); - let val = builder.ins().popcnt(arg); - state.push1(builder.ins().sextend(I32, val)); + state.push1(builder.ins().popcnt(arg)); } Operator::I64Popcnt => { let arg = state.pop1(); - let val = builder.ins().popcnt(arg); - state.push1(builder.ins().sextend(I64, val)); + state.push1(builder.ins().popcnt(arg)); } Operator::I64ExtendSI32 => { let val = state.pop1();