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.
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,
)

View File

@@ -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);
}

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(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