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:
parent
79d9ae7a77
commit
ea10026b64
@ -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,
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
56
llvm/test/CodeGen/LoongArch/issue163681.ll
Normal file
56
llvm/test/CodeGen/LoongArch/issue163681.ll
Normal 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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user