[llvm] [MachineVerifier] Query TargetInstrInfo for PHI nodes. (PR #110507)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 30 06:19:22 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-spir-v
Author: Nathan Gauër (Keenuts)
<details>
<summary>Changes</summary>
MachineVerifier can run post-ISel, and the SPIR-V can emit phi nodes. This means G_PHI/PHI are not the only 2 opcodes representing phi nodes. In addition, the SPIR-V instruction operands are ordered slightly differently (1 additional operand before the pairs).
There are multiple ways to solve this, but here I decided to go with the less invasive, but less generic method. However the refactoring mentioned in the rest of this commit can still be done later, as it also relies on TII exposing an `isPhiInstr`.
Alternative designs
===================
1st alternative:
----------------
Add a bit in MCInstDesc for phi instructions (See #<!-- -->110019). This was refused as the MCInstDesc bits are "pricy", and a that only impacts 3 instructions is not a wise expense.
2nd alternative:
----------------
Refactorize MachineInstr::isPHI() to use TargetInstrInfo. This was almost possible, as MachineInstr often has a parent MBB, and thus a parent MF which links to the TargetInstrInfo.
In MachineInstr: this->getMF().getTargetInstrInfo().isPhiInstr(*this)
This however breaks as soon as the MachineInstr is removed from it's parent block.
Solving this requires passing TII as a parameter to isPHI. Passing TII requires each code using `isPHI` to also pass TII. This is a very invasive change, which impacts almost every backends, and several passes.
Fixes #<!-- -->108844
---
Full diff: https://github.com/llvm/llvm-project/pull/110507.diff
4 Files Affected:
- (modified) llvm/include/llvm/CodeGen/TargetInstrInfo.h (+26)
- (modified) llvm/lib/CodeGen/MachineVerifier.cpp (+19-14)
- (modified) llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp (+24)
- (modified) llvm/lib/Target/SPIRV/SPIRVInstrInfo.h (+5)
``````````diff
diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
index 07b59b241d9f9a..b4bf8ca984c9f4 100644
--- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
@@ -2278,6 +2278,32 @@ class TargetInstrInfo : public MCInstrInfo {
llvm_unreachable("unknown number of operands necessary");
}
+ // This this instruction a PHI node.
+ // This function should be favored over MI.isPHI() as it allows backends to
+ // define additional PHI instructions.
+ virtual bool isPhiInstr(const MachineInstr &MI) const {
+ return MI.getOpcode() == TargetOpcode::G_PHI ||
+ MI.getOpcode() == TargetOpcode::PHI;
+ }
+
+ // Returns the number of [Value, Src] pairs this phi instruction has.
+ // Only valid to call on a phi node.
+ virtual unsigned getNumPhiIncomingPair(const MachineInstr &MI) const {
+ assert(isPhiInstr(MI));
+ // G_PHI/PHI only have a single operand before the pairs. 2 Operands per
+ // pair.
+ return (MI.getNumOperands() - 1) / 2;
+ }
+
+ // Returns the |index|'th [Value, Src] pair of this phi instruction.
+ virtual std::pair<MachineOperand, MachineBasicBlock *>
+ getPhiIncomingPair(const MachineInstr &MI, unsigned index) const {
+ // Behavior for G_PHI/PHI instructions.
+ MachineOperand Operand = MI.getOperand(index * 2 + 1);
+ MachineBasicBlock *MBB = MI.getOperand(index * 2 + 2).getMBB();
+ return {Operand, MBB};
+ }
+
private:
mutable std::unique_ptr<MIRFormatter> Formatter;
unsigned CallFrameSetupOpcode, CallFrameDestroyOpcode;
diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index 24a0f41775cc1d..eb9d67fe9d14cf 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -2202,7 +2202,7 @@ void MachineVerifier::visitMachineInstrBefore(const MachineInstr *MI) {
if (MI->getFlag(MachineInstr::NoConvergent) && !MCID.isConvergent())
report("NoConvergent flag expected only on convergent instructions.", MI);
- if (MI->isPHI()) {
+ if (TII->isPhiInstr(*MI)) {
if (MF->getProperties().hasProperty(
MachineFunctionProperties::Property::NoPHIs))
report("Found PHI instruction with NoPHIs property set", MI);
@@ -2705,7 +2705,7 @@ MachineVerifier::visitMachineOperand(const MachineOperand *MO, unsigned MONum) {
break;
case MachineOperand::MO_MachineBasicBlock:
- if (MI->isPHI() && !MO->getMBB()->isSuccessor(MI->getParent()))
+ if (TII->isPhiInstr(*MI) && !MO->getMBB()->isSuccessor(MI->getParent()))
report("PHI operand is not in the CFG", MO, MONum);
break;
@@ -2776,7 +2776,7 @@ void MachineVerifier::checkLivenessAtUse(const MachineOperand *MO,
}
LiveQueryResult LRQ = LR.Query(UseIdx);
- bool HasValue = LRQ.valueIn() || (MI->isPHI() && LRQ.valueOut());
+ bool HasValue = LRQ.valueIn() || (TII->isPhiInstr(*MI) && LRQ.valueOut());
// Check if we have a segment at the use, note however that we only need one
// live subregister range, the others may be dead.
if (!HasValue && LaneMask.none()) {
@@ -2895,7 +2895,7 @@ void MachineVerifier::checkLiveness(const MachineOperand *MO, unsigned MONum) {
// Check LiveInts liveness and kill.
if (LiveInts && !LiveInts->isNotInMIMap(*MI)) {
SlotIndex UseIdx;
- if (MI->isPHI()) {
+ if (TII->isPhiInstr(*MI)) {
// PHI use occurs on the edge, so check for live out here instead.
UseIdx = LiveInts->getMBBEndIdx(
MI->getOperand(MONum + 1).getMBB()).getPrevSlot();
@@ -2926,7 +2926,7 @@ void MachineVerifier::checkLiveness(const MachineOperand *MO, unsigned MONum) {
continue;
checkLivenessAtUse(MO, MONum, UseIdx, SR, Reg, SR.LaneMask);
LiveQueryResult LRQ = SR.Query(UseIdx);
- if (LRQ.valueIn() || (MI->isPHI() && LRQ.valueOut()))
+ if (LRQ.valueIn() || (TII->isPhiInstr(*MI) && LRQ.valueOut()))
LiveInMask |= SR.LaneMask;
}
// At least parts of the register has to be live at the use.
@@ -2936,7 +2936,7 @@ void MachineVerifier::checkLiveness(const MachineOperand *MO, unsigned MONum) {
report_context(UseIdx);
}
// For PHIs all lanes should be live
- if (MI->isPHI() && LiveInMask != MOMask) {
+ if (TII->isPhiInstr(*MI) && LiveInMask != MOMask) {
report("Not all lanes of PHI source live at use", MO, MONum);
report_context(*LI);
report_context(UseIdx);
@@ -2987,7 +2987,7 @@ void MachineVerifier::checkLiveness(const MachineOperand *MO, unsigned MONum) {
// must be live in. PHI instructions are handled separately.
if (MInfo.regsKilled.count(Reg))
report("Using a killed virtual register", MO, MONum);
- else if (!MI->isPHI())
+ else if (!TII->isPhiInstr(*MI))
MInfo.vregsLiveIn.insert(std::make_pair(Reg, MI));
}
}
@@ -3211,17 +3211,22 @@ void MachineVerifier::calcRegsRequired() {
todo.insert(Pred);
}
- // Handle the PHI node.
- for (const MachineInstr &MI : MBB.phis()) {
- for (unsigned i = 1, e = MI.getNumOperands(); i != e; i += 2) {
+ // Handle the PHI nodes.
+ // Note: MBB.phis() only returns range containing PHI/G_PHI instructions.
+ // MachineVerifier checks MIR post-ISel, and some backends do have their own
+ // phi nodes.
+ for (const MachineInstr &MI : MBB) {
+ // PHI nodes must be the first instructions of the MBB.
+ if (!TII->isPhiInstr(MI))
+ break;
+ for (unsigned i = 0; i < TII->getNumPhiIncomingPair(MI); ++i) {
+ auto [Op, Pred] = TII->getPhiIncomingPair(MI, i);
// Skip those Operands which are undef regs or not regs.
- if (!MI.getOperand(i).isReg() || !MI.getOperand(i).readsReg())
+ if (!Op.isReg() || !Op.readsReg())
continue;
// Get register and predecessor for one PHI edge.
- Register Reg = MI.getOperand(i).getReg();
- const MachineBasicBlock *Pred = MI.getOperand(i + 1).getMBB();
-
+ Register Reg = Op.getReg();
BBInfo &PInfo = MBBInfoMap[Pred];
if (PInfo.addRequired(Reg))
todo.insert(Pred);
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp
index e8a6b4fdbae977..0029bcb830e381 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp
@@ -267,3 +267,27 @@ bool SPIRVInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
}
return false;
}
+
+// The SPIR-V backends can emit the OpPhi instruction.
+bool SPIRVInstrInfo::isPhiInstr(const MachineInstr &MI) const {
+ return TargetInstrInfo::isPhiInstr(MI) || MI.getOpcode() == SPIRV::OpPhi;
+}
+
+unsigned SPIRVInstrInfo::getNumPhiIncomingPair(const MachineInstr &MI) const {
+ // OpPhi has 2 operands before the [Value, Src] pairs.
+ if (MI.getOpcode() == SPIRV::OpPhi)
+ return (MI.getNumOperands() - 2) / 2;
+ return TargetInstrInfo::getNumPhiIncomingPair(MI);
+}
+
+std::pair<MachineOperand, MachineBasicBlock *>
+SPIRVInstrInfo::getPhiIncomingPair(const MachineInstr &MI,
+ unsigned index) const {
+ if (MI.getOpcode() != SPIRV::OpPhi)
+ return TargetInstrInfo::getPhiIncomingPair(MI, index);
+
+ // Skip the first 2 operands (dst, type), and access each [Value, Src] pairs.
+ MachineOperand Operand = MI.getOperand(index * 2 + 2);
+ MachineBasicBlock *MBB = MI.getOperand(index * 2 + 3).getMBB();
+ return {Operand, MBB};
+}
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h
index 67d2d979cb5a15..bae008adaa85c9 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h
@@ -54,6 +54,11 @@ class SPIRVInstrInfo : public SPIRVGenInstrInfo {
bool KillSrc, bool RenamableDest = false,
bool RenamableSrc = false) const override;
bool expandPostRAPseudo(MachineInstr &MI) const override;
+
+ bool isPhiInstr(const MachineInstr &MI) const override;
+ unsigned getNumPhiIncomingPair(const MachineInstr &MI) const override;
+ std::pair<MachineOperand, MachineBasicBlock *>
+ getPhiIncomingPair(const MachineInstr &MI, unsigned index) const override;
};
namespace SPIRV {
``````````
</details>
https://github.com/llvm/llvm-project/pull/110507
More information about the llvm-commits
mailing list