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:
@@ -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,
|
||||
@@ -2150,6 +2160,8 @@ pub(crate) fn define(
|
||||
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
|
||||
subtraction.
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
let imm = imm_const(&mut pos, arg, imm, is_signed);
|
||||
let replace = pos.func.dfg.replace(inst);
|
||||
match opcode {
|
||||
// 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);
|
||||
ir::Opcode::BandImm => {
|
||||
replace.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);
|
||||
ir::Opcode::BorImm => {
|
||||
replace.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);
|
||||
ir::Opcode::BxorImm => {
|
||||
replace.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
|
||||
InstructionData::BinaryImm64 {
|
||||
opcode: ir::Opcode::IshlImm,
|
||||
arg,
|
||||
imm,
|
||||
} => {
|
||||
let imm = pos.ins().iconst(I32, imm);
|
||||
pos.func.dfg.replace(inst).ishl(arg, imm);
|
||||
ir::Opcode::IshlImm => {
|
||||
replace.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);
|
||||
ir::Opcode::RotlImm => {
|
||||
replace.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);
|
||||
ir::Opcode::RotrImm => {
|
||||
replace.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);
|
||||
ir::Opcode::SshrImm => {
|
||||
replace.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);
|
||||
ir::Opcode::UshrImm => {
|
||||
replace.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
|
||||
ir::Opcode::IaddImm => {
|
||||
replace.iadd(arg, imm);
|
||||
}
|
||||
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);
|
||||
ir::Opcode::IrsubImm => {
|
||||
// note: arg order reversed
|
||||
replace.isub(imm, arg);
|
||||
}
|
||||
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);
|
||||
ir::Opcode::ImulImm => {
|
||||
replace.imul(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);
|
||||
ir::Opcode::SdivImm => {
|
||||
replace.sdiv(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);
|
||||
ir::Opcode::SremImm => {
|
||||
replace.srem(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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user