diff --git a/cranelift/codegen/meta/src/shared/instructions.rs b/cranelift/codegen/meta/src/shared/instructions.rs index 83c2a1de94..482664dc60 100644 --- a/cranelift/codegen/meta/src/shared/instructions.rs +++ b/cranelift/codegen/meta/src/shared/instructions.rs @@ -1801,7 +1801,7 @@ pub(crate) fn define( Compare scalar integer to a constant. This is the same as the `icmp` instruction, except one operand is - an immediate constant. + a sign extended 64 bit immediate constant. This instruction can only compare scalars. Use `icmp` for lane-wise vector comparisons. @@ -2060,7 +2060,7 @@ pub(crate) fn define( r#" Add immediate integer. - Same as `iadd`, but one operand is an immediate constant. + Same as `iadd`, but one operand is a sign extended 64 bit immediate constant. Polymorphic over all scalar integer types, but does not support vector types. @@ -2077,6 +2077,8 @@ pub(crate) fn define( r#" Integer multiplication by immediate constant. + Same as `imul`, but one operand is a sign extended 64 bit immediate constant. + Polymorphic over all scalar integer types, but does not support vector types. "#, @@ -2092,6 +2094,8 @@ pub(crate) fn define( r#" Unsigned integer division by an immediate constant. + Same as `udiv`, but one operand is a zero extended 64 bit immediate constant. + This operation traps if the divisor is zero. "#, &formats.binary_imm64, @@ -2106,6 +2110,8 @@ pub(crate) fn define( r#" Signed integer division by an immediate constant. + Same as `sdiv`, but one operand is a sign extended 64 bit immediate constant. + This operation traps if the divisor is zero, or if the result is not representable in `B` bits two's complement. This only happens when `x = -2^{B-1}, Y = -1`. @@ -2122,6 +2128,8 @@ pub(crate) fn define( r#" Unsigned integer remainder with immediate divisor. + Same as `urem`, but one operand is a zero extended 64 bit immediate constant. + This operation traps if the divisor is zero. "#, &formats.binary_imm64, @@ -2136,6 +2144,8 @@ pub(crate) fn define( r#" Signed integer remainder with immediate divisor. + Same as `srem`, but one operand is a sign extended 64 bit immediate constant. + This operation traps if the divisor is zero. "#, &formats.binary_imm64, @@ -2149,6 +2159,8 @@ pub(crate) fn define( "irsub_imm", r#" Immediate reverse wrapping subtraction: `a := Y - x \pmod{2^B}`. + + The immediate operand is a sign extended 64 bit constant. Also works as integer negation when `Y = 0`. Use `iadd_imm` with a negative immediate operand for the reverse immediate @@ -2552,7 +2564,7 @@ pub(crate) fn define( r#" Bitwise and with immediate. - Same as `band`, but one operand is an immediate constant. + Same as `band`, but one operand is a zero extended 64 bit immediate constant. Polymorphic over all scalar integer types, but does not support vector types. @@ -2569,7 +2581,7 @@ pub(crate) fn define( r#" Bitwise or with immediate. - Same as `bor`, but one operand is an immediate constant. + Same as `bor`, but one operand is a zero extended 64 bit immediate constant. Polymorphic over all scalar integer types, but does not support vector types. @@ -2586,7 +2598,7 @@ pub(crate) fn define( r#" Bitwise xor with immediate. - Same as `bxor`, but one operand is an immediate constant. + Same as `bxor`, but one operand is a zero extended 64 bit immediate constant. Polymorphic over all scalar integer types, but does not support vector types. @@ -2635,6 +2647,8 @@ pub(crate) fn define( "rotl_imm", r#" Rotate left by immediate. + + Same as `rotl`, but one operand is a zero extended 64 bit immediate constant. "#, &formats.binary_imm64, ) @@ -2647,6 +2661,8 @@ pub(crate) fn define( "rotr_imm", r#" Rotate right by immediate. + + Same as `rotr`, but one operand is a zero extended 64 bit immediate constant. "#, &formats.binary_imm64, ) diff --git a/cranelift/codegen/src/legalizer/mod.rs b/cranelift/codegen/src/legalizer/mod.rs index ae7caf0345..07557b8de3 100644 --- a/cranelift/codegen/src/legalizer/mod.rs +++ b/cranelift/codegen/src/legalizer/mod.rs @@ -15,8 +15,9 @@ use crate::cursor::{Cursor, FuncCursor}; use crate::flowgraph::ControlFlowGraph; -use crate::ir::types::I32; -use crate::ir::{self, InstBuilder, InstructionData, MemFlags}; +use crate::ir::immediates::Imm64; +use crate::ir::types::{I128, I64}; +use crate::ir::{self, InstBuilder, InstructionData, MemFlags, Value}; use crate::isa::TargetIsa; mod globalvalue; @@ -27,6 +28,21 @@ use self::globalvalue::expand_global_value; use self::heap::expand_heap_addr; use self::table::expand_table_addr; +fn imm_const(pos: &mut FuncCursor, arg: Value, imm: Imm64, is_signed: bool) -> Value { + let ty = pos.func.dfg.value_type(arg); + match (ty, is_signed) { + (I128, true) => { + let imm = pos.ins().iconst(I64, imm); + pos.ins().sextend(I128, imm) + } + (I128, false) => { + let imm = pos.ins().iconst(I64, imm); + pos.ins().uextend(I128, imm) + } + _ => pos.ins().iconst(ty.lane_type(), imm), + } +} + /// Perform a simple legalization by expansion of the function, without /// platform-specific transforms. pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa) { @@ -157,160 +173,85 @@ pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa: offset, } => expand_table_addr(isa, inst, &mut pos.func, table, arg, offset), - // bitops - InstructionData::BinaryImm64 { - opcode: ir::Opcode::BandImm, - arg, - imm, - } => { - let ty = pos.func.dfg.value_type(arg); - let imm = pos.ins().iconst(ty, imm); - pos.func.dfg.replace(inst).band(arg, imm); - } - InstructionData::BinaryImm64 { - opcode: ir::Opcode::BorImm, - arg, - imm, - } => { - let ty = pos.func.dfg.value_type(arg); - let imm = pos.ins().iconst(ty, imm); - pos.func.dfg.replace(inst).bor(arg, imm); - } - InstructionData::BinaryImm64 { - opcode: ir::Opcode::BxorImm, - arg, - imm, - } => { - let ty = pos.func.dfg.value_type(arg); - let imm = pos.ins().iconst(ty, imm); - pos.func.dfg.replace(inst).bxor(arg, imm); - } - InstructionData::BinaryImm64 { - opcode: ir::Opcode::IaddImm, - arg, - imm, - } => { - let ty = pos.func.dfg.value_type(arg); - let imm = pos.ins().iconst(ty, imm); - pos.func.dfg.replace(inst).iadd(arg, imm); - } + InstructionData::BinaryImm64 { opcode, arg, imm } => { + let is_signed = match opcode { + ir::Opcode::IaddImm + | ir::Opcode::IrsubImm + | ir::Opcode::ImulImm + | ir::Opcode::SdivImm + | ir::Opcode::SremImm + | ir::Opcode::IfcmpImm => true, + _ => false, + }; - // bitshifting - InstructionData::BinaryImm64 { - opcode: ir::Opcode::IshlImm, - arg, - imm, - } => { - let imm = pos.ins().iconst(I32, imm); - pos.func.dfg.replace(inst).ishl(arg, imm); - } - InstructionData::BinaryImm64 { - opcode: ir::Opcode::RotlImm, - arg, - imm, - } => { - let imm = pos.ins().iconst(I32, imm); - pos.func.dfg.replace(inst).rotl(arg, imm); - } - InstructionData::BinaryImm64 { - opcode: ir::Opcode::RotrImm, - arg, - imm, - } => { - let imm = pos.ins().iconst(I32, imm); - pos.func.dfg.replace(inst).rotr(arg, imm); - } - InstructionData::BinaryImm64 { - opcode: ir::Opcode::SshrImm, - arg, - imm, - } => { - let imm = pos.ins().iconst(I32, imm); - pos.func.dfg.replace(inst).sshr(arg, imm); - } - InstructionData::BinaryImm64 { - opcode: ir::Opcode::UshrImm, - arg, - imm, - } => { - let imm = pos.ins().iconst(I32, imm); - pos.func.dfg.replace(inst).ushr(arg, imm); - } - - // math - InstructionData::BinaryImm64 { - opcode: ir::Opcode::IrsubImm, - arg, - imm, - } => { - let ty = pos.func.dfg.value_type(arg); - let imm = pos.ins().iconst(ty, imm); - pos.func.dfg.replace(inst).isub(imm, arg); // note: arg order reversed - } - InstructionData::BinaryImm64 { - opcode: ir::Opcode::ImulImm, - arg, - imm, - } => { - let ty = pos.func.dfg.value_type(arg); - let imm = pos.ins().iconst(ty, imm); - pos.func.dfg.replace(inst).imul(arg, imm); - } - InstructionData::BinaryImm64 { - opcode: ir::Opcode::SdivImm, - arg, - imm, - } => { - let ty = pos.func.dfg.value_type(arg); - let imm = pos.ins().iconst(ty, imm); - pos.func.dfg.replace(inst).sdiv(arg, imm); - } - InstructionData::BinaryImm64 { - opcode: ir::Opcode::SremImm, - arg, - imm, - } => { - let ty = pos.func.dfg.value_type(arg); - let imm = pos.ins().iconst(ty, imm); - pos.func.dfg.replace(inst).srem(arg, imm); - } - InstructionData::BinaryImm64 { - opcode: ir::Opcode::UdivImm, - arg, - imm, - } => { - let ty = pos.func.dfg.value_type(arg); - let imm = pos.ins().iconst(ty, imm); - pos.func.dfg.replace(inst).udiv(arg, imm); - } - InstructionData::BinaryImm64 { - opcode: ir::Opcode::UremImm, - arg, - imm, - } => { - let ty = pos.func.dfg.value_type(arg); - let imm = pos.ins().iconst(ty, imm); - pos.func.dfg.replace(inst).urem(arg, imm); + let imm = imm_const(&mut pos, arg, imm, is_signed); + let replace = pos.func.dfg.replace(inst); + match opcode { + // bitops + ir::Opcode::BandImm => { + replace.band(arg, imm); + } + ir::Opcode::BorImm => { + replace.bor(arg, imm); + } + ir::Opcode::BxorImm => { + replace.bxor(arg, imm); + } + // bitshifting + ir::Opcode::IshlImm => { + replace.ishl(arg, imm); + } + ir::Opcode::RotlImm => { + replace.rotl(arg, imm); + } + ir::Opcode::RotrImm => { + replace.rotr(arg, imm); + } + ir::Opcode::SshrImm => { + replace.sshr(arg, imm); + } + ir::Opcode::UshrImm => { + replace.ushr(arg, imm); + } + // math + ir::Opcode::IaddImm => { + replace.iadd(arg, imm); + } + ir::Opcode::IrsubImm => { + // note: arg order reversed + replace.isub(imm, arg); + } + ir::Opcode::ImulImm => { + replace.imul(arg, imm); + } + ir::Opcode::SdivImm => { + replace.sdiv(arg, imm); + } + ir::Opcode::SremImm => { + replace.srem(arg, imm); + } + ir::Opcode::UdivImm => { + replace.udiv(arg, imm); + } + ir::Opcode::UremImm => { + replace.urem(arg, imm); + } + // comparisons + ir::Opcode::IfcmpImm => { + replace.ifcmp(arg, imm); + } + _ => prev_pos = pos.position(), + }; } // comparisons - InstructionData::BinaryImm64 { - opcode: ir::Opcode::IfcmpImm, - arg, - imm, - } => { - let ty = pos.func.dfg.value_type(arg); - let imm = pos.ins().iconst(ty, imm); - pos.func.dfg.replace(inst).ifcmp(arg, imm); - } InstructionData::IntCompareImm { opcode: ir::Opcode::IcmpImm, cond, arg, imm, } => { - let ty = pos.func.dfg.value_type(arg); - let imm = pos.ins().iconst(ty, imm); + let imm = imm_const(&mut pos, arg, imm, true); pos.func.dfg.replace(inst).icmp(cond, arg, imm); } diff --git a/cranelift/filetests/filetests/runtests/i128-arithmetic.clif b/cranelift/filetests/filetests/runtests/i128-arithmetic.clif index 08fe16dc95..6898b3eb71 100644 --- a/cranelift/filetests/filetests/runtests/i128-arithmetic.clif +++ b/cranelift/filetests/filetests/runtests/i128-arithmetic.clif @@ -56,3 +56,13 @@ block0(v0: i128,v1: i128): ; run: %mul_i128(13, 0x01010101_01010101_01010101_01010101) == 0x0D0D0D0D_0D0D0D0D_0D0D0D0D_0D0D0D0D ; run: %mul_i128(0x00000000_01234567_89ABCDEF_00000000, 0x00000000_FEDCBA98_76543210_00000000) == 0x2236D88F_E5618CF0_00000000_00000000 ; run: %mul_i128(0xC0FFEEEE_C0FFEEEE_C0FFEEEE_C0FFEEEE, 0xDECAFFFF_DECAFFFF_DECAFFFF_DECAFFFF) == 0x5ECD38B5_9D1C2B7E_DB6B1E48_19BA1112 + + +; Tests that imm's are sign extended on i128's +; See: https://github.com/bytecodealliance/wasmtime/issues/4568 +function %iadd_imm_neg(i128) -> i128 { +block0(v0: i128): + v1 = iadd_imm.i128 v0, -1 + return v1 +} +; run: %iadd_imm_neg(1) == 0