Reland "[LoongArch] Add isSafeToMove hook to prevent unsafe instruction motion" (#167465)

This patch introduces a new virtual method
`TargetInstrInfo::isSafeToMove()` to allow backends to control whether a
machine instruction can be safely moved by optimization passes.

The `BranchFolder` pass now respects this hook when hoisting common
code. By default, all instructions are considered safe to to move.

For LoongArch, `isSafeToMove()` is overridden to prevent
relocation-related instruction sequences (e.g. PC-relative addressing
and calls) from being broken by instruction motion. Correspondingly,
`isSchedulingBoundary()` is updated to reuse this logic for consistency.

Relands #163725
This commit is contained in:
hev 2025-11-12 08:51:08 +08:00 committed by GitHub
parent 79d9ae7a77
commit ea10026b64
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 102 additions and 18 deletions

View File

@ -1765,6 +1765,17 @@ public:
return true;
}
/// Return true if it's safe to move a machine instruction.
/// This allows the backend to prevent certain special instruction
/// sequences from being broken by instruction motion in optimization
/// passes.
/// By default, this returns true for every instruction.
virtual bool isSafeToMove(const MachineInstr &MI,
const MachineBasicBlock *MBB,
const MachineFunction &MF) const {
return true;
}
/// Test if the given instruction should be considered a scheduling boundary.
/// This primarily includes labels and terminators.
virtual bool isSchedulingBoundary(const MachineInstr &MI,

View File

@ -1979,6 +1979,7 @@ bool BranchFolder::HoistCommonCodeInSuccs(MachineBasicBlock *MBB) {
MachineBasicBlock::iterator FIB = FBB->begin();
MachineBasicBlock::iterator TIE = TBB->end();
MachineBasicBlock::iterator FIE = FBB->end();
MachineFunction &MF = *TBB->getParent();
while (TIB != TIE && FIB != FIE) {
// Skip dbg_value instructions. These do not count.
TIB = skipDebugInstructionsForward(TIB, TIE, false);
@ -1993,6 +1994,10 @@ bool BranchFolder::HoistCommonCodeInSuccs(MachineBasicBlock *MBB) {
// Hard to reason about register liveness with predicated instruction.
break;
if (!TII->isSafeToMove(*TIB, TBB, MF))
// Don't hoist the instruction if it isn't safe to move.
break;
bool IsSafe = true;
for (MachineOperand &MO : TIB->operands()) {
// Don't attempt to hoist instructions with register masks.

View File

@ -378,12 +378,9 @@ bool LoongArchInstrInfo::isBranchOffsetInRange(unsigned BranchOp,
}
}
bool LoongArchInstrInfo::isSchedulingBoundary(const MachineInstr &MI,
const MachineBasicBlock *MBB,
const MachineFunction &MF) const {
if (TargetInstrInfo::isSchedulingBoundary(MI, MBB, MF))
return true;
bool LoongArchInstrInfo::isSafeToMove(const MachineInstr &MI,
const MachineBasicBlock *MBB,
const MachineFunction &MF) const {
auto MII = MI.getIterator();
auto MIE = MBB->end();
@ -429,25 +426,25 @@ bool LoongArchInstrInfo::isSchedulingBoundary(const MachineInstr &MI,
auto MO2 = Lu32I->getOperand(2).getTargetFlags();
if (MO0 == LoongArchII::MO_PCREL_HI && MO1 == LoongArchII::MO_PCREL_LO &&
MO2 == LoongArchII::MO_PCREL64_LO)
return true;
return false;
if ((MO0 == LoongArchII::MO_GOT_PC_HI || MO0 == LoongArchII::MO_LD_PC_HI ||
MO0 == LoongArchII::MO_GD_PC_HI) &&
MO1 == LoongArchII::MO_GOT_PC_LO && MO2 == LoongArchII::MO_GOT_PC64_LO)
return true;
return false;
if (MO0 == LoongArchII::MO_IE_PC_HI && MO1 == LoongArchII::MO_IE_PC_LO &&
MO2 == LoongArchII::MO_IE_PC64_LO)
return true;
return false;
if (MO0 == LoongArchII::MO_DESC_PC_HI &&
MO1 == LoongArchII::MO_DESC_PC_LO &&
MO2 == LoongArchII::MO_DESC64_PC_LO)
return true;
return false;
break;
}
case LoongArch::LU52I_D: {
auto MO = MI.getOperand(2).getTargetFlags();
if (MO == LoongArchII::MO_PCREL64_HI || MO == LoongArchII::MO_GOT_PC64_HI ||
MO == LoongArchII::MO_IE_PC64_HI || MO == LoongArchII::MO_DESC64_PC_HI)
return true;
return false;
break;
}
default:
@ -487,7 +484,7 @@ bool LoongArchInstrInfo::isSchedulingBoundary(const MachineInstr &MI,
auto MO1 = LoongArchII::getDirectFlags(SecondOp->getOperand(2));
auto MO2 = LoongArchII::getDirectFlags(Ld->getOperand(2));
if (MO1 == LoongArchII::MO_DESC_PC_LO && MO2 == LoongArchII::MO_DESC_LD)
return true;
return false;
break;
}
if (SecondOp == MIE ||
@ -496,34 +493,34 @@ bool LoongArchInstrInfo::isSchedulingBoundary(const MachineInstr &MI,
auto MO1 = LoongArchII::getDirectFlags(SecondOp->getOperand(2));
if (MO0 == LoongArchII::MO_PCREL_HI && SecondOp->getOpcode() == AddiOp &&
MO1 == LoongArchII::MO_PCREL_LO)
return true;
return false;
if (MO0 == LoongArchII::MO_GOT_PC_HI && SecondOp->getOpcode() == LdOp &&
MO1 == LoongArchII::MO_GOT_PC_LO)
return true;
return false;
if ((MO0 == LoongArchII::MO_LD_PC_HI ||
MO0 == LoongArchII::MO_GD_PC_HI) &&
SecondOp->getOpcode() == AddiOp && MO1 == LoongArchII::MO_GOT_PC_LO)
return true;
return false;
break;
}
case LoongArch::ADDI_W:
case LoongArch::ADDI_D: {
auto MO = LoongArchII::getDirectFlags(MI.getOperand(2));
if (MO == LoongArchII::MO_PCREL_LO || MO == LoongArchII::MO_GOT_PC_LO)
return true;
return false;
break;
}
case LoongArch::LD_W:
case LoongArch::LD_D: {
auto MO = LoongArchII::getDirectFlags(MI.getOperand(2));
if (MO == LoongArchII::MO_GOT_PC_LO)
return true;
return false;
break;
}
case LoongArch::PseudoDESC_CALL: {
auto MO = LoongArchII::getDirectFlags(MI.getOperand(2));
if (MO == LoongArchII::MO_DESC_CALL)
return true;
return false;
break;
}
default:
@ -531,6 +528,18 @@ bool LoongArchInstrInfo::isSchedulingBoundary(const MachineInstr &MI,
}
}
return true;
}
bool LoongArchInstrInfo::isSchedulingBoundary(const MachineInstr &MI,
const MachineBasicBlock *MBB,
const MachineFunction &MF) const {
if (TargetInstrInfo::isSchedulingBoundary(MI, MBB, MF))
return true;
if (!isSafeToMove(MI, MBB, MF))
return true;
return false;
}

View File

@ -66,6 +66,9 @@ public:
bool isBranchOffsetInRange(unsigned BranchOpc,
int64_t BrOffset) const override;
bool isSafeToMove(const MachineInstr &MI, const MachineBasicBlock *MBB,
const MachineFunction &MF) const override;
bool isSchedulingBoundary(const MachineInstr &MI,
const MachineBasicBlock *MBB,
const MachineFunction &MF) const override;

View File

@ -0,0 +1,56 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
; RUN: llc --mtriple=loongarch64 -code-model=large --verify-machineinstrs < %s \
; RUN: | FileCheck %s
@.str = external constant [1 x i8]
define void @caller(ptr %0) {
; CHECK-LABEL: caller:
; CHECK: # %bb.0:
; CHECK-NEXT: addi.d $sp, $sp, -16
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: st.d $ra, $sp, 8 # 8-byte Folded Spill
; CHECK-NEXT: .cfi_offset 1, -8
; CHECK-NEXT: ld.w $a2, $zero, 0
; CHECK-NEXT: ld.d $a1, $a0, 0
; CHECK-NEXT: beqz $a2, .LBB0_2
; CHECK-NEXT: # %bb.1:
; CHECK-NEXT: pcalau12i $a0, %got_pc_hi20(.str)
; CHECK-NEXT: addi.d $a2, $zero, %got_pc_lo12(.str)
; CHECK-NEXT: lu32i.d $a2, %got64_pc_lo20(.str)
; CHECK-NEXT: lu52i.d $a2, $a2, %got64_pc_hi12(.str)
; CHECK-NEXT: ldx.d $a2, $a2, $a0
; CHECK-NEXT: move $a0, $zero
; CHECK-NEXT: jirl $ra, $zero, 0
; CHECK-NEXT: b .LBB0_3
; CHECK-NEXT: .LBB0_2:
; CHECK-NEXT: pcalau12i $a0, %got_pc_hi20(.str)
; CHECK-NEXT: addi.d $a2, $zero, %got_pc_lo12(.str)
; CHECK-NEXT: lu32i.d $a2, %got64_pc_lo20(.str)
; CHECK-NEXT: lu52i.d $a2, $a2, %got64_pc_hi12(.str)
; CHECK-NEXT: ldx.d $a2, $a2, $a0
; CHECK-NEXT: move $a0, $zero
; CHECK-NEXT: move $a3, $zero
; CHECK-NEXT: jirl $ra, $zero, 0
; CHECK-NEXT: .LBB0_3:
; CHECK-NEXT: st.d $zero, $zero, 0
; CHECK-NEXT: ld.d $ra, $sp, 8 # 8-byte Folded Reload
; CHECK-NEXT: addi.d $sp, $sp, 16
; CHECK-NEXT: ret
%2 = load i32, ptr null, align 4
%3 = icmp eq i32 %2, 0
%4 = load i64, ptr %0, align 8
br i1 %3, label %6, label %5
5: ; preds = %1
call void null(ptr null, i64 %4, ptr @.str)
br label %7
6: ; preds = %1
tail call void null(ptr null, i64 %4, ptr @.str, i32 0)
br label %7
7: ; preds = %6, %5
store ptr null, ptr null, align 8
ret void
}