Skip to content

Commit 0683b84

Browse files
authored
Cranelift: Stop sign-extending Imm64 immediates in builder methods (bytecodealliance#9046)
* Cranelift: Stop sign-extending `Imm64` immediates in builder methods Originally, we sign-extended `Imm64` immediates from their controlling type var's width to `i64` in the builder methods for `BinaryImm64` and a couple other instruction formats: bytecodealliance#1687 At some point, we decided that the unused bits in the `Imm64` when the controlling type var's width is less than 64 should be zero. And we started asserting that in various places, for example: https://github.com/bytecodealliance/wasmtime/blob/87817f38a128caa76eaa6a3c3c8ceac81a329a3e/cranelift/codegen/src/machinst/lower.rs#L1237-L1243 However, we never removed or updated the sign-extension code in the builder methods. This commit switches the builder methods over to masking the `Imm64` to just the valid bits, effectively doing a zero extend from the controlling type var's width instead of a sign extend. To avoid too much churn in tests, I made `display_inst` print the sign-extended-from-the-controlling-type-variable's-width value of `Imm64` immediates. I also made the `Display` implementation for `Imm64` always print in decimal form with a `-` for negative numbers, rather than the hex it would previously print for large negative numbers, so that `iconst.i32 0x8000_0000` doesn't display as `iconst.i32 0xffff_ffff_8000_0000`, which is otherwise pretty confusing. * Rename condition * return new immediates instead of mutating * Update some test expectations
1 parent 86bd6c8 commit 0683b84

File tree

12 files changed

+148
-70
lines changed

12 files changed

+148
-70
lines changed

cranelift/codegen/meta/src/gen_inst.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -973,7 +973,7 @@ fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) {
973973
args.join(", ")
974974
);
975975

976-
let imms_need_sign_extension = format
976+
let imms_need_masking = format
977977
.imm_fields
978978
.iter()
979979
.any(|f| f.kind.rust_type == "ir::immediates::Imm64");
@@ -986,7 +986,7 @@ fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) {
986986
fmtln!(
987987
fmt,
988988
"let{} data = ir::InstructionData::{} {{",
989-
if imms_need_sign_extension { " mut" } else { "" },
989+
if imms_need_masking { " mut" } else { "" },
990990
format.name
991991
);
992992
fmt.indent(|fmt| {
@@ -995,8 +995,8 @@ fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) {
995995
});
996996
fmtln!(fmt, "};");
997997

998-
if imms_need_sign_extension {
999-
fmtln!(fmt, "data.sign_extend_immediates(ctrl_typevar);");
998+
if imms_need_masking {
999+
fmtln!(fmt, "data.mask_immediates(ctrl_typevar);");
10001000
}
10011001

10021002
// Assert that this opcode belongs to this format

cranelift/codegen/src/ir/immediates.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,38 @@ impl Imm64 {
7474
self.0
7575
}
7676

77+
/// Mask this immediate to the given power-of-two bit width.
78+
#[must_use]
79+
pub(crate) fn mask_to_width(&self, bit_width: u32) -> Self {
80+
debug_assert!(bit_width.is_power_of_two());
81+
82+
if bit_width >= 64 {
83+
return *self;
84+
}
85+
86+
let bit_width = i64::from(bit_width);
87+
let mask = (1 << bit_width) - 1;
88+
let masked = self.0 & mask;
89+
Imm64(masked)
90+
}
91+
7792
/// Sign extend this immediate as if it were a signed integer of the given
7893
/// power-of-two width.
79-
pub fn sign_extend_from_width(&mut self, bit_width: u32) {
80-
debug_assert!(bit_width.is_power_of_two());
94+
#[must_use]
95+
pub(crate) fn sign_extend_from_width(&self, bit_width: u32) -> Self {
96+
debug_assert!(
97+
bit_width.is_power_of_two(),
98+
"{bit_width} is not a power of two"
99+
);
81100

82101
if bit_width >= 64 {
83-
return;
102+
return *self;
84103
}
85104

86105
let bit_width = i64::from(bit_width);
87106
let delta = 64 - bit_width;
88107
let sign_extended = (self.0 << delta) >> delta;
89-
*self = Imm64(sign_extended);
108+
Imm64(sign_extended)
90109
}
91110
}
92111

@@ -111,8 +130,8 @@ impl From<i64> for Imm64 {
111130
impl Display for Imm64 {
112131
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
113132
let x = self.0;
114-
if -10_000 < x && x < 10_000 {
115-
// Use decimal for small numbers.
133+
if x < 10_000 {
134+
// Use decimal for small and negative numbers.
116135
write!(f, "{}", x)
117136
} else {
118137
write_hex(x as u64, f)
@@ -1093,7 +1112,7 @@ mod tests {
10931112
assert_eq!(Imm64(9999).to_string(), "9999");
10941113
assert_eq!(Imm64(10000).to_string(), "0x2710");
10951114
assert_eq!(Imm64(-9999).to_string(), "-9999");
1096-
assert_eq!(Imm64(-10000).to_string(), "0xffff_ffff_ffff_d8f0");
1115+
assert_eq!(Imm64(-10000).to_string(), "-10000");
10971116
assert_eq!(Imm64(0xffff).to_string(), "0xffff");
10981117
assert_eq!(Imm64(0x10000).to_string(), "0x0001_0000");
10991118
}
@@ -1113,6 +1132,7 @@ mod tests {
11131132
}
11141133

11151134
// Verify that `text` can be parsed as a `T` into a value that displays as `want`.
1135+
#[track_caller]
11161136
fn parse_ok<T: FromStr + Display>(text: &str, want: &str)
11171137
where
11181138
<T as FromStr>::Err: Display,
@@ -1146,11 +1166,11 @@ mod tests {
11461166

11471167
// Probe limits.
11481168
parse_ok::<Imm64>("0xffffffff_ffffffff", "-1");
1149-
parse_ok::<Imm64>("0x80000000_00000000", "0x8000_0000_0000_0000");
1150-
parse_ok::<Imm64>("-0x80000000_00000000", "0x8000_0000_0000_0000");
1169+
parse_ok::<Imm64>("0x80000000_00000000", "-9223372036854775808");
1170+
parse_ok::<Imm64>("-0x80000000_00000000", "-9223372036854775808");
11511171
parse_err::<Imm64>("-0x80000000_00000001", "Negative number too small");
11521172
parse_ok::<Imm64>("18446744073709551615", "-1");
1153-
parse_ok::<Imm64>("-9223372036854775808", "0x8000_0000_0000_0000");
1173+
parse_ok::<Imm64>("-9223372036854775808", "-9223372036854775808");
11541174
// Overflow both the `checked_add` and `checked_mul`.
11551175
parse_err::<Imm64>("18446744073709551616", "Too large decimal number");
11561176
parse_err::<Imm64>("184467440737095516100", "Too large decimal number");

cranelift/codegen/src/ir/instructions.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -450,21 +450,24 @@ impl InstructionData {
450450
}
451451

452452
#[inline]
453-
pub(crate) fn sign_extend_immediates(&mut self, ctrl_typevar: Type) {
453+
pub(crate) fn mask_immediates(&mut self, ctrl_typevar: Type) {
454454
if ctrl_typevar.is_invalid() {
455455
return;
456456
}
457457

458458
let bit_width = ctrl_typevar.bits();
459459

460460
match self {
461+
Self::UnaryImm { opcode: _, imm } => {
462+
*imm = imm.mask_to_width(bit_width);
463+
}
461464
Self::BinaryImm64 {
462465
opcode,
463466
arg: _,
464467
imm,
465468
} => {
466469
if *opcode == Opcode::SdivImm || *opcode == Opcode::SremImm {
467-
imm.sign_extend_from_width(bit_width);
470+
*imm = imm.mask_to_width(bit_width);
468471
}
469472
}
470473
Self::IntCompareImm {
@@ -475,7 +478,7 @@ impl InstructionData {
475478
} => {
476479
debug_assert_eq!(*opcode, Opcode::IcmpImm);
477480
if cond.unsigned() != *cond {
478-
imm.sign_extend_from_width(bit_width);
481+
*imm = imm.mask_to_width(bit_width);
479482
}
480483
}
481484
_ => {}

cranelift/codegen/src/isle_prelude.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,8 @@ macro_rules! isle_common_prelude_methods {
193193
}
194194

195195
#[inline]
196-
fn i64_sextend_imm64(&mut self, ty: Type, mut x: Imm64) -> i64 {
197-
x.sign_extend_from_width(ty.bits());
198-
x.bits()
196+
fn i64_sextend_imm64(&mut self, ty: Type, x: Imm64) -> i64 {
197+
x.sign_extend_from_width(ty.bits()).bits()
199198
}
200199

201200
#[inline]

cranelift/codegen/src/write.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -390,13 +390,20 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt
390390
let pool = &dfg.value_lists;
391391
let jump_tables = &dfg.jump_tables;
392392
use crate::ir::instructions::InstructionData::*;
393+
let ctrl_ty = dfg.ctrl_typevar(inst);
393394
match dfg.insts[inst] {
394395
AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]),
395396
AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
396397
LoadNoOffset { flags, arg, .. } => write!(w, "{} {}", flags, arg),
397398
StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]),
398399
Unary { arg, .. } => write!(w, " {}", arg),
399-
UnaryImm { imm, .. } => write!(w, " {}", imm),
400+
UnaryImm { imm, .. } => write!(w, " {}", {
401+
let mut imm = imm;
402+
if ctrl_ty.bits() != 0 {
403+
imm = imm.sign_extend_from_width(ctrl_ty.bits());
404+
}
405+
imm
406+
}),
400407
UnaryIeee16 { imm, .. } => write!(w, " {}", imm),
401408
UnaryIeee32 { imm, .. } => write!(w, " {}", imm),
402409
UnaryIeee64 { imm, .. } => write!(w, " {}", imm),
@@ -406,7 +413,13 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt
406413
} => write!(w, " {}", constant_handle),
407414
Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
408415
BinaryImm8 { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
409-
BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
416+
BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, {
417+
let mut imm = imm;
418+
if ctrl_ty.bits() != 0 {
419+
imm = imm.sign_extend_from_width(ctrl_ty.bits());
420+
}
421+
imm
422+
}),
410423
Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
411424
MultiAry { ref args, .. } => {
412425
if args.is_empty() {
@@ -424,7 +437,13 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt
424437
write!(w, " {}, {}, {}", args[0], args[1], data)
425438
}
426439
IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
427-
IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm),
440+
IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, {
441+
let mut imm = imm;
442+
if ctrl_ty.bits() != 0 {
443+
imm = imm.sign_extend_from_width(ctrl_ty.bits());
444+
}
445+
imm
446+
}),
428447
IntAddTrap { args, code, .. } => write!(w, " {}, {}, {}", args[0], args[1], code),
429448
FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
430449
Jump { destination, .. } => {
@@ -495,7 +514,13 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt
495514
for arg in dfg.inst_values(inst) {
496515
if let ValueDef::Result(src, _) = dfg.value_def(arg) {
497516
let imm = match dfg.insts[src] {
498-
UnaryImm { imm, .. } => imm.to_string(),
517+
UnaryImm { imm, .. } => {
518+
let mut imm = imm;
519+
if dfg.ctrl_typevar(src).bits() != 0 {
520+
imm = imm.sign_extend_from_width(dfg.ctrl_typevar(src).bits());
521+
}
522+
imm.to_string()
523+
}
499524
UnaryIeee16 { imm, .. } => imm.to_string(),
500525
UnaryIeee32 { imm, .. } => imm.to_string(),
501526
UnaryIeee64 { imm, .. } => imm.to_string(),

cranelift/filetests/filetests/egraph/cprop.clif

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ block0:
1919
return v2
2020
}
2121

22-
; check: v3 = iconst.i16 0xfffe
22+
; check: v3 = iconst.i16 -2
2323
; nextln: return v3
2424

2525
function %ishl() -> i8 {
@@ -96,7 +96,7 @@ block0:
9696
return v2
9797
}
9898

99-
; check: v3 = iconst.i8 252
99+
; check: v3 = iconst.i8 -4
100100
; check: return v3
101101

102102
function %sshr_i8_i16() -> i8 {
@@ -107,7 +107,7 @@ block0:
107107
return v2
108108
}
109109

110-
; check: v3 = iconst.i8 252
110+
; check: v3 = iconst.i8 -4
111111
; check: return v3
112112

113113
function %sshr_i16_i8() -> i16 {
@@ -118,7 +118,7 @@ block0:
118118
return v2
119119
}
120120

121-
; check: v3 = iconst.i16 0xfffc
121+
; check: v3 = iconst.i16 -4
122122
; check: return v3
123123

124124
function %icmp_eq_i32() -> i8 {
@@ -238,7 +238,7 @@ block0:
238238
return v2
239239
}
240240

241-
; check: v3 = iconst.i8 246
241+
; check: v3 = iconst.i8 -10
242242
; nextln: return v3
243243

244244
function %sextend_iconst() -> i32 {
@@ -248,7 +248,7 @@ block0:
248248
return v2
249249
}
250250

251-
; check: v3 = iconst.i32 0xffff_fff6
251+
; check: v3 = iconst.i32 -10
252252
; nextln: return v3
253253

254254
function %uextend_iconst() -> i32 {
@@ -310,7 +310,7 @@ block0:
310310
return v1
311311
}
312312

313-
; check: v2 = iconst.i64 0xf0de_bc9a_7856_3412
313+
; check: v2 = iconst.i64 -1090226688147180526
314314
; nextln: return v2
315315

316316
function %f16_fmin() -> f16 {

0 commit comments

Comments
 (0)