Fix spillslot reload of narrow values: zero-extend, don't sign-extend.
Previously, the x64 backend's ABI code would generate a sign-extending load when loading a less-than-64-bit integer from a spillslot. This is incorrect: e.g., for i32s > 0x80000000, this would result in all high bits set. This interacts poorly with another optimization. Normally, the invariant is that the high bits of a register holding a value of a certain type, beyond that type's bits, are undefined. However, as an optimization, we recognize and use the fact that on x86-64, 32-bit instructions zero the upper 32 bits. This allows us to elide a 32-to-64-bit zero-extend op (turning it into just a move, which can then sometimes disappear entirely due to register coalescing). If a spill and reload happen between the production of a 32-bit value from an instruction known to zero the upper bits and its use, then we will rely on zero upper bits that might actually be set by a sign-extend. This will result in incorrect execution. As a fix, we stick to a simple invariant: we always spill and reload a full 64 bits when handling integer registers on x64. This ensures that no bits are mangled.
This commit is contained in:
@@ -317,19 +317,19 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
||||
}
|
||||
|
||||
fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Self::I {
|
||||
let ext_kind = match ty {
|
||||
// For integer-typed values, we always load a full 64 bits (and we always spill a full 64
|
||||
// bits as well -- see `Inst::store()`).
|
||||
let ty = match ty {
|
||||
types::B1
|
||||
| types::B8
|
||||
| types::I8
|
||||
| types::B16
|
||||
| types::I16
|
||||
| types::B32
|
||||
| types::I32 => ExtKind::SignExtend,
|
||||
types::B64 | types::I64 | types::R64 | types::F32 | types::F64 => ExtKind::None,
|
||||
_ if ty.bytes() == 16 => ExtKind::None,
|
||||
_ => panic!("load_stack({})", ty),
|
||||
| types::I32 => types::I64,
|
||||
_ => ty,
|
||||
};
|
||||
Inst::load(ty, mem, into_reg, ext_kind)
|
||||
Inst::load(ty, mem, into_reg, ExtKind::None)
|
||||
}
|
||||
|
||||
fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Self::I {
|
||||
|
||||
@@ -1033,6 +1033,7 @@ impl fmt::Display for Avx512Opcode {
|
||||
/// This defines the ways a value can be extended: either signed- or zero-extension, or none for
|
||||
/// types that are not extended. Contrast with [ExtMode], which defines the widths from and to which
|
||||
/// values can be extended.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum ExtKind {
|
||||
None,
|
||||
|
||||
Reference in New Issue
Block a user