Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions cranelift/codegen/meta/src/shared/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1799,7 +1799,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.
Expand Down Expand Up @@ -2058,7 +2058,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.
Expand All @@ -2075,6 +2075,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.
"#,
Expand All @@ -2090,6 +2092,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,
Expand All @@ -2104,6 +2108,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`.
Expand All @@ -2120,6 +2126,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,
Expand All @@ -2134,6 +2142,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,
Expand All @@ -2147,6 +2157,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
Expand Down Expand Up @@ -2550,7 +2562,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.
Expand All @@ -2567,7 +2579,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.
Expand All @@ -2584,7 +2596,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.
Expand Down Expand Up @@ -2633,6 +2645,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,
)
Expand All @@ -2645,6 +2659,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,
)
Expand Down
235 changes: 88 additions & 147 deletions cranelift/codegen/src/legalizer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}

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

Expand Down
10 changes: 10 additions & 0 deletions cranelift/filetests/filetests/runtests/i128-arithmetic.clif
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,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