x64 lowering fix: i32.popcnt should not merge load and make it 64-bit.
As a subtle consequence of the recent load-op fusion, popcnt of a value that came from a load.i32 was compiling into a 64-bit load. This is a result of the way in which x86 infers the width of loads: it is a consequence of the instruction containing the memory reference, not the memory reference itself. So the `input_to_reg_mem()` helper (convert an instruction input into a register or memory reference) was providing the appropriate memory reference for the result of a load.i32, but never encoded the assumption that it would only be used in a 32-bit instruction. It turns out that popcnt.i32 uses a 64-bit instruction to load this RM op, hence widening a 32-bit to 64-bit load (which is problematic when the offset is (memory_length - 4)). Separately, popcnt was using the RM operand twice, resulting in two loads if we merged a load. This isn't a correctness bug in practice because only a racy sequence (store interleaving between the loads) would produce incorrect results, but we decided earlier to treat loads as effectful for now, neither reordering nor duplicating them, to deliberately reduce complexity. Because of the second issue, the fix is just to force the operand into a register always, so any source load will not be merged. Discovered via fuzzing with oss-fuzz.
This commit is contained in:
@@ -1530,7 +1530,10 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
let src = if let Some(ext_spec) = ext_spec {
|
||||
RegMem::reg(extend_input_to_reg(ctx, inputs[0], ext_spec))
|
||||
} else {
|
||||
input_to_reg_mem(ctx, inputs[0])
|
||||
// N.B.: explicitly put input in a reg here because the width of the instruction
|
||||
// into which this RM op goes may not match the width of the input type (in fact,
|
||||
// it won't for i32.popcnt), and we don't want a larger than necessary load.
|
||||
RegMem::reg(put_input_in_reg(ctx, inputs[0]))
|
||||
};
|
||||
let dst = get_output_reg(ctx, outputs[0]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user