s390x: Move the value out of the casloop_val_reg with mov_preg (#5430)

The casloop_emit function in the s390x backend was using the fixed non-allocatable register %r0 directly with move instructions, which produced a panic in the regalloc2 checker (#5425). This PR changes the casloop_result function to use mov_preg instead of copy_reg to fetch the result, as it's not viewed by regalloc2 as a move.

Fixes #5425
This commit is contained in:
Trevor Elliott
2022-12-14 13:06:35 -08:00
committed by GitHub
parent 8383e4b6bd
commit 9dc4f1a83c
5 changed files with 37 additions and 7 deletions

View File

@@ -2843,6 +2843,9 @@
(decl preg_stack () PReg) (decl preg_stack () PReg)
(extern constructor preg_stack preg_stack) (extern constructor preg_stack preg_stack)
(decl preg_gpr_0 () PReg)
(extern constructor preg_gpr_0 preg_gpr_0)
;; Copy the physical stack register into a virtual register. ;; Copy the physical stack register into a virtual register.
(decl sp () Reg) (decl sp () Reg)
(rule (sp) (rule (sp)
@@ -3414,7 +3417,7 @@
;; be written if the memory location still holds the old value in %r0. ;; be written if the memory location still holds the old value in %r0.
;; The result should be passed to "casloop_result" or (in the case of ;; The result should be passed to "casloop_result" or (in the case of
;; subword loops) to "casloop_rotate_result". ;; subword loops) to "casloop_rotate_result".
(decl casloop_emit (VecMInstBuilder Type MemFlags Reg Reg) Reg) (decl casloop_emit (VecMInstBuilder Type MemFlags Reg Reg) PReg)
(rule (casloop_emit ib ty flags aligned_addr val) (rule (casloop_emit ib ty flags aligned_addr val)
(let (;; Construct a memory argument for the aligned word. (let (;; Construct a memory argument for the aligned word.
(aligned_mem MemArg (memarg_reg_plus_off aligned_addr 0 0 flags)) (aligned_mem MemArg (memarg_reg_plus_off aligned_addr 0 0 flags))
@@ -3424,17 +3427,23 @@
;; Emit initial load followed by compare-and-swap loop. ;; Emit initial load followed by compare-and-swap loop.
(_ Unit (emit_load (ty_ext32 ty) (casloop_val_reg) aligned_mem)) (_ Unit (emit_load (ty_ext32 ty) (casloop_val_reg) aligned_mem))
(_ Unit (emit_loop ib (intcc_as_cond (IntCC.NotEqual))))) (_ Unit (emit_loop ib (intcc_as_cond (IntCC.NotEqual)))))
result))
;; push_atomic_cas above returns its destination register argument,
;; cas_loop_val_reg, as its result. As cas_loop_val_reg is a writable
;; version of `gpr 0`, we return that directly here as a physical
;; register to avoid accidentally using it with a non-preg move
;; instruction.
(preg_gpr_0)))
;; Compute the previous memory value after a (fullword) compare-and-swap loop. ;; Compute the previous memory value after a (fullword) compare-and-swap loop.
;; In the big-endian case, the value is already correct, but may need to be ;; In the big-endian case, the value is already correct, but may need to be
;; copied out of the hard register. In the little-endian case, we need to ;; copied out of the hard register. In the little-endian case, we need to
;; byte-swap since the compare-and-swap instruction is always big-endian. ;; byte-swap since the compare-and-swap instruction is always big-endian.
(decl casloop_result (Type MemFlags Reg) Reg) (decl casloop_result (Type MemFlags PReg) Reg)
(rule 1 (casloop_result (ty_32_or_64 ty) (bigendian) result) (rule 1 (casloop_result (ty_32_or_64 ty) (bigendian) result)
(copy_reg ty result)) (mov_preg result))
(rule (casloop_result (ty_32_or_64 ty) (littleendian) result) (rule (casloop_result (ty_32_or_64 ty) (littleendian) result)
(bswap_reg ty result)) (bswap_reg ty (preg_to_reg result)))
;; Emit a fullword compare-and-swap loop, returning the previous memory value. ;; Emit a fullword compare-and-swap loop, returning the previous memory value.
(decl casloop (VecMInstBuilder Type MemFlags Reg Reg) Reg) (decl casloop (VecMInstBuilder Type MemFlags Reg Reg) Reg)

View File

@@ -2350,7 +2350,7 @@ impl Inst {
} }
&Inst::MovPReg { rd, rm } => { &Inst::MovPReg { rd, rm } => {
let rm: Reg = rm.into(); let rm: Reg = rm.into();
debug_assert!([regs::gpr(15)].contains(&rm)); debug_assert!([regs::gpr(0), regs::gpr(14), regs::gpr(15)].contains(&rm));
let rd = allocs.next_writable(rd); let rd = allocs.next_writable(rd);
Inst::Mov64 { rd, rm }.emit(&[], sink, emit_info, state); Inst::Mov64 { rd, rm }.emit(&[], sink, emit_info, state);
} }

View File

@@ -620,7 +620,7 @@ fn s390x_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandC
collector.reg_use(rm); collector.reg_use(rm);
} }
&Inst::MovPReg { rd, rm } => { &Inst::MovPReg { rd, rm } => {
debug_assert!([regs::gpr(14), regs::gpr(15)].contains(&rm.into())); debug_assert!([regs::gpr(0), regs::gpr(14), regs::gpr(15)].contains(&rm.into()));
debug_assert!(rd.to_reg().is_virtual()); debug_assert!(rd.to_reg().is_virtual());
collector.reg_def(rd); collector.reg_def(rd);
} }

View File

@@ -938,6 +938,11 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, S390xBackend> {
stack_reg().to_real_reg().unwrap().into() stack_reg().to_real_reg().unwrap().into()
} }
#[inline]
fn preg_gpr_0(&mut self) -> PReg {
gpr(0).to_real_reg().unwrap().into()
}
#[inline] #[inline]
fn writable_regpair(&mut self, hi: WritableReg, lo: WritableReg) -> WritableRegPair { fn writable_regpair(&mut self, hi: WritableReg, lo: WritableReg) -> WritableRegPair {
WritableRegPair { hi, lo } WritableRegPair { hi, lo }

View File

@@ -0,0 +1,16 @@
test compile
set regalloc_checker=1
target s390x
function %a() system_v {
fn0 = %callee_f64(i64) -> i32
block0:
v1 = iconst.i64 0
v2 = call fn0(v1) ; v1 = 0
v21 = iconst.i64 0
v22 = iconst.i32 2
v23 = atomic_rmw.i32 xchg v21, v22 ; v21 = 0, v22 = 2
trap user0
}