cranelift: Sign extend immediates in instructions that embed them. (#4602)

* cranelift: Sign extend immediates in instructions that embed them.

* cranelift: Clarify imm instruction behaviour

* cranelift: Deduplicate imm_const

* cranelift: zero extend logical imm ops
This commit is contained in:
Afonso Bordado
2022-08-15 19:08:20 +01:00
committed by GitHub
parent c6d2a3f94e
commit e577a76c0d
3 changed files with 118 additions and 151 deletions

View File

@@ -1801,7 +1801,7 @@ pub(crate) fn define(
Compare scalar integer to a constant. Compare scalar integer to a constant.
This is the same as the `icmp` instruction, except one operand is 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 This instruction can only compare scalars. Use `icmp` for
lane-wise vector comparisons. lane-wise vector comparisons.
@@ -2060,7 +2060,7 @@ pub(crate) fn define(
r#" r#"
Add immediate integer. 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 Polymorphic over all scalar integer types, but does not support vector
types. types.
@@ -2077,6 +2077,8 @@ pub(crate) fn define(
r#" r#"
Integer multiplication by immediate constant. 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 Polymorphic over all scalar integer types, but does not support vector
types. types.
"#, "#,
@@ -2092,6 +2094,8 @@ pub(crate) fn define(
r#" r#"
Unsigned integer division by an immediate constant. 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. This operation traps if the divisor is zero.
"#, "#,
&formats.binary_imm64, &formats.binary_imm64,
@@ -2106,6 +2110,8 @@ pub(crate) fn define(
r#" r#"
Signed integer division by an immediate constant. 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 This operation traps if the divisor is zero, or if the result is not
representable in `B` bits two's complement. This only happens representable in `B` bits two's complement. This only happens
when `x = -2^{B-1}, Y = -1`. when `x = -2^{B-1}, Y = -1`.
@@ -2122,6 +2128,8 @@ pub(crate) fn define(
r#" r#"
Unsigned integer remainder with immediate divisor. 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. This operation traps if the divisor is zero.
"#, "#,
&formats.binary_imm64, &formats.binary_imm64,
@@ -2136,6 +2144,8 @@ pub(crate) fn define(
r#" r#"
Signed integer remainder with immediate divisor. 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. This operation traps if the divisor is zero.
"#, "#,
&formats.binary_imm64, &formats.binary_imm64,
@@ -2149,6 +2159,8 @@ pub(crate) fn define(
"irsub_imm", "irsub_imm",
r#" r#"
Immediate reverse wrapping subtraction: `a := Y - x \pmod{2^B}`. 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` Also works as integer negation when `Y = 0`. Use `iadd_imm`
with a negative immediate operand for the reverse immediate with a negative immediate operand for the reverse immediate
@@ -2552,7 +2564,7 @@ pub(crate) fn define(
r#" r#"
Bitwise and with immediate. 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 Polymorphic over all scalar integer types, but does not support vector
types. types.
@@ -2569,7 +2581,7 @@ pub(crate) fn define(
r#" r#"
Bitwise or with immediate. 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 Polymorphic over all scalar integer types, but does not support vector
types. types.
@@ -2586,7 +2598,7 @@ pub(crate) fn define(
r#" r#"
Bitwise xor with immediate. 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 Polymorphic over all scalar integer types, but does not support vector
types. types.
@@ -2635,6 +2647,8 @@ pub(crate) fn define(
"rotl_imm", "rotl_imm",
r#" r#"
Rotate left by immediate. Rotate left by immediate.
Same as `rotl`, but one operand is a zero extended 64 bit immediate constant.
"#, "#,
&formats.binary_imm64, &formats.binary_imm64,
) )
@@ -2647,6 +2661,8 @@ pub(crate) fn define(
"rotr_imm", "rotr_imm",
r#" r#"
Rotate right by immediate. Rotate right by immediate.
Same as `rotr`, but one operand is a zero extended 64 bit immediate constant.
"#, "#,
&formats.binary_imm64, &formats.binary_imm64,
) )

View File

@@ -15,8 +15,9 @@
use crate::cursor::{Cursor, FuncCursor}; use crate::cursor::{Cursor, FuncCursor};
use crate::flowgraph::ControlFlowGraph; use crate::flowgraph::ControlFlowGraph;
use crate::ir::types::I32; use crate::ir::immediates::Imm64;
use crate::ir::{self, InstBuilder, InstructionData, MemFlags}; use crate::ir::types::{I128, I64};
use crate::ir::{self, InstBuilder, InstructionData, MemFlags, Value};
use crate::isa::TargetIsa; use crate::isa::TargetIsa;
mod globalvalue; mod globalvalue;
@@ -27,6 +28,21 @@ use self::globalvalue::expand_global_value;
use self::heap::expand_heap_addr; use self::heap::expand_heap_addr;
use self::table::expand_table_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 /// Perform a simple legalization by expansion of the function, without
/// platform-specific transforms. /// platform-specific transforms.
pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa) { 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, offset,
} => expand_table_addr(isa, inst, &mut pos.func, table, arg, offset), } => expand_table_addr(isa, inst, &mut pos.func, table, arg, offset),
// bitops InstructionData::BinaryImm64 { opcode, arg, imm } => {
InstructionData::BinaryImm64 { let is_signed = match opcode {
opcode: ir::Opcode::BandImm, ir::Opcode::IaddImm
arg, | ir::Opcode::IrsubImm
imm, | ir::Opcode::ImulImm
} => { | ir::Opcode::SdivImm
let ty = pos.func.dfg.value_type(arg); | ir::Opcode::SremImm
let imm = pos.ins().iconst(ty, imm); | ir::Opcode::IfcmpImm => true,
pos.func.dfg.replace(inst).band(arg, imm); _ => false,
} };
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);
}
// bitshifting let imm = imm_const(&mut pos, arg, imm, is_signed);
InstructionData::BinaryImm64 { let replace = pos.func.dfg.replace(inst);
opcode: ir::Opcode::IshlImm, match opcode {
arg, // bitops
imm, ir::Opcode::BandImm => {
} => { replace.band(arg, imm);
let imm = pos.ins().iconst(I32, imm); }
pos.func.dfg.replace(inst).ishl(arg, imm); ir::Opcode::BorImm => {
} replace.bor(arg, imm);
InstructionData::BinaryImm64 { }
opcode: ir::Opcode::RotlImm, ir::Opcode::BxorImm => {
arg, replace.bxor(arg, imm);
imm, }
} => { // bitshifting
let imm = pos.ins().iconst(I32, imm); ir::Opcode::IshlImm => {
pos.func.dfg.replace(inst).rotl(arg, imm); replace.ishl(arg, imm);
} }
InstructionData::BinaryImm64 { ir::Opcode::RotlImm => {
opcode: ir::Opcode::RotrImm, replace.rotl(arg, imm);
arg, }
imm, ir::Opcode::RotrImm => {
} => { replace.rotr(arg, imm);
let imm = pos.ins().iconst(I32, imm); }
pos.func.dfg.replace(inst).rotr(arg, imm); ir::Opcode::SshrImm => {
} replace.sshr(arg, imm);
InstructionData::BinaryImm64 { }
opcode: ir::Opcode::SshrImm, ir::Opcode::UshrImm => {
arg, replace.ushr(arg, imm);
imm, }
} => { // math
let imm = pos.ins().iconst(I32, imm); ir::Opcode::IaddImm => {
pos.func.dfg.replace(inst).sshr(arg, imm); replace.iadd(arg, imm);
} }
InstructionData::BinaryImm64 { ir::Opcode::IrsubImm => {
opcode: ir::Opcode::UshrImm, // note: arg order reversed
arg, replace.isub(imm, arg);
imm, }
} => { ir::Opcode::ImulImm => {
let imm = pos.ins().iconst(I32, imm); replace.imul(arg, imm);
pos.func.dfg.replace(inst).ushr(arg, imm); }
} ir::Opcode::SdivImm => {
replace.sdiv(arg, imm);
// math }
InstructionData::BinaryImm64 { ir::Opcode::SremImm => {
opcode: ir::Opcode::IrsubImm, replace.srem(arg, imm);
arg, }
imm, ir::Opcode::UdivImm => {
} => { replace.udiv(arg, imm);
let ty = pos.func.dfg.value_type(arg); }
let imm = pos.ins().iconst(ty, imm); ir::Opcode::UremImm => {
pos.func.dfg.replace(inst).isub(imm, arg); // note: arg order reversed replace.urem(arg, imm);
} }
InstructionData::BinaryImm64 { // comparisons
opcode: ir::Opcode::ImulImm, ir::Opcode::IfcmpImm => {
arg, replace.ifcmp(arg, imm);
imm, }
} => { _ => prev_pos = pos.position(),
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);
} }
// comparisons // 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 { InstructionData::IntCompareImm {
opcode: ir::Opcode::IcmpImm, opcode: ir::Opcode::IcmpImm,
cond, cond,
arg, arg,
imm, imm,
} => { } => {
let ty = pos.func.dfg.value_type(arg); let imm = imm_const(&mut pos, arg, imm, true);
let imm = pos.ins().iconst(ty, imm);
pos.func.dfg.replace(inst).icmp(cond, arg, imm); pos.func.dfg.replace(inst).icmp(cond, arg, imm);
} }

View File

@@ -56,3 +56,13 @@ block0(v0: i128,v1: i128):
; run: %mul_i128(13, 0x01010101_01010101_01010101_01010101) == 0x0D0D0D0D_0D0D0D0D_0D0D0D0D_0D0D0D0D ; 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(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 ; 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