-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[SPIR-V][Codegen] Add isPhi bit to MCInstrDesc/MIR definitions #110019
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
the isPHI() function relied on the MIR opcode. This was fine as AFAIK no real target has a real PHI instruction. With SPIR-V, this assumption breaks. The MachineVerifier has a special handling for PHI instructions to check liveness, but since this relied on the PHI/G_PHI opcode check, it was raising an error when the OpPhi MIR was checked. Since the SPIR-V opcode is specific to the backend, I don't think checking the actual opcode in the MachineVerifier code is correct, so I added a bit in the instruction description, and applied it to the 3 existing PHI instruction I found (G_PHI, PHI, OpPhi). Another different bit is the index of the first BB/reg pair: %res = PHI [reg, BB] %res = OpPhi %reg [reg, BB] The solution I had is to have a function in the MachineInstr returning the start index, allowing the MachineVerifier to work on both formats. Its slightly better, it works, but it's not THAT great. An alternative could be to add the index in the MCInstrDesc, this way, the index bit definition would be in the TD files, closer to the definition. This patch reduces the amount of failling tests with EXPENSIVE_CHECKS from 120 to 113 (All fixed are in the SPIR-V backend). Fixes llvm#108844 Signed-off-by: Nathan Gauër <[email protected]>
@llvm/pr-subscribers-backend-spir-v Author: Nathan Gauër (Keenuts) Changesthe isPHI() function relied on the MIR opcode. This was fine as AFAIK no real target has a real PHI instruction. With SPIR-V, this assumption breaks. Since the SPIR-V opcode is specific to the backend, I don't think checking the actual opcode in the MachineVerifier code is correct, so I added a bit in the instruction description, and applied it to the 3 existing PHI instruction I found (G_PHI, PHI, OpPhi). Another different bit is the index of the first BB/reg pair: %res = PHI [reg, BB] The solution I had is to have a function in the MachineInstr returning the start index, allowing the MachineVerifier to work on both formats. Its slightly better, it works, but it's not THAT great. An alternative could be to add the index in the MCInstrDesc, this way, the index bit definition would be in the TD files, closer to the definition. This patch reduces the amount of failling tests with EXPENSIVE_CHECKS from 120 to 113 (All fixed are in the SPIR-V backend). Fixes #108844 Full diff: https://github.com/llvm/llvm-project/pull/110019.diff 9 Files Affected:
diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h
index 76a7b8662bae66..14ad5531f22c87 100644
--- a/llvm/include/llvm/CodeGen/MachineInstr.h
+++ b/llvm/include/llvm/CodeGen/MachineInstr.h
@@ -1394,10 +1394,19 @@ class MachineInstr
return getOpcode() == TargetOpcode::JUMP_TABLE_DEBUG_INFO;
}
- bool isPHI() const {
- return getOpcode() == TargetOpcode::PHI ||
- getOpcode() == TargetOpcode::G_PHI;
+ bool isPHI() const { return getDesc().isPhi(); }
+
+ unsigned getIndexFirstPHIPair() const {
+ assert(isPHI());
+
+ if (getOpcode() == TargetOpcode::G_PHI || getOpcode() == TargetOpcode::PHI)
+ return 1;
+ // The only other instruction marked as PHI node is OpPhi, in the SPIR-V
+ // backend. The only difference is the [reg, BB] pairs starts at index 2,
+ // not 1.
+ return 2;
}
+
bool isKill() const { return getOpcode() == TargetOpcode::KILL; }
bool isImplicitDef() const { return getOpcode()==TargetOpcode::IMPLICIT_DEF; }
bool isInlineAsm() const {
diff --git a/llvm/include/llvm/MC/MCInstrDesc.h b/llvm/include/llvm/MC/MCInstrDesc.h
index ef0b3c0a73992b..3e7786bf2a3d8e 100644
--- a/llvm/include/llvm/MC/MCInstrDesc.h
+++ b/llvm/include/llvm/MC/MCInstrDesc.h
@@ -187,6 +187,7 @@ enum Flag {
Trap,
VariadicOpsAreDefs,
Authenticated,
+ Phi,
};
} // namespace MCID
@@ -292,6 +293,10 @@ class MCInstrDesc {
/// unconditional branches and return instructions.
bool isBarrier() const { return Flags & (1ULL << MCID::Barrier); }
+ /// Returns true if this instruction acts as a PHI node.
+ /// Not all PHI operands need to dominates the definition.
+ bool isPhi() const { return Flags & (1ULL << MCID::Phi); }
+
/// Returns true if this instruction part of the terminator for
/// a basic block. Typically this is things like return and branch
/// instructions.
diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td
index f5e62dda6fd043..b8a4b9bba3cf67 100644
--- a/llvm/include/llvm/Target/GenericOpcodes.td
+++ b/llvm/include/llvm/Target/GenericOpcodes.td
@@ -96,6 +96,7 @@ def G_PHI : GenericInstruction {
let OutOperandList = (outs type0:$dst);
let InOperandList = (ins variable_ops);
let hasSideEffects = false;
+ let isPhi = true;
}
def G_FRAME_INDEX : GenericInstruction {
diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td
index 3e037affe1cfd2..2b459f0df4228b 100644
--- a/llvm/include/llvm/Target/Target.td
+++ b/llvm/include/llvm/Target/Target.td
@@ -635,6 +635,7 @@ class Instruction : InstructionEncoding {
bit isBitcast = false; // Is this instruction a bitcast instruction?
bit isSelect = false; // Is this instruction a select instruction?
bit isBarrier = false; // Can control flow fall through this instruction?
+ bit isPhi = false; // Is this instruction a phi instruction?
bit isCall = false; // Is this instruction a call instruction?
bit isAdd = false; // Is this instruction an add instruction?
bit isTrap = false; // Is this instruction a trap instruction?
@@ -1174,6 +1175,7 @@ def PHI : StandardPseudoInstruction {
let InOperandList = (ins variable_ops);
let AsmString = "PHINODE";
let hasSideEffects = false;
+ let isPhi = true;
}
def INLINEASM : StandardPseudoInstruction {
let OutOperandList = (outs);
diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index 24a0f41775cc1d..ed843e7a7550f7 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -3213,7 +3213,8 @@ void MachineVerifier::calcRegsRequired() {
// Handle the PHI node.
for (const MachineInstr &MI : MBB.phis()) {
- for (unsigned i = 1, e = MI.getNumOperands(); i != e; i += 2) {
+ for (unsigned i = MI.getIndexFirstPHIPair(), e = MI.getNumOperands();
+ i != e; i += 2) {
// Skip those Operands which are undef regs or not regs.
if (!MI.getOperand(i).isReg() || !MI.getOperand(i).readsReg())
continue;
@@ -3268,7 +3269,8 @@ void MachineVerifier::checkPHIOps(const MachineBasicBlock &MBB) {
if (!DefReg.isVirtual())
report("Expected first PHI operand to be a virtual register", &MODef, 0);
- for (unsigned I = 1, E = Phi.getNumOperands(); I != E; I += 2) {
+ for (unsigned I = Phi.getIndexFirstPHIPair(), E = Phi.getNumOperands();
+ I != E; I += 2) {
const MachineOperand &MO0 = Phi.getOperand(I);
if (!MO0.isReg()) {
report("Expected PHI operand to be a register", &MO0, I);
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
index 51bacb00b1c515..6af0c857b1c582 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
@@ -615,8 +615,10 @@ def OpFwidthCoarse: UnOp<"OpFwidthCoarse", 215>;
// 3.42.17 Control-Flow Instructions
-def OpPhi: Op<245, (outs ID:$res), (ins TYPE:$type, ID:$var0, ID:$block0, variable_ops),
- "$res = OpPhi $type $var0 $block0">;
+let isPhi = 1 in {
+ def OpPhi: Op<245, (outs ID:$res), (ins TYPE:$type, ID:$var0, ID:$block0, variable_ops),
+ "$res = OpPhi $type $var0 $block0">;
+}
def OpLoopMerge: Op<246, (outs), (ins unknown:$merge, unknown:$continue, LoopControl:$lc, variable_ops),
"OpLoopMerge $merge $continue $lc">;
def OpSelectionMerge: Op<247, (outs), (ins unknown:$merge, SelectionControl:$sc),
diff --git a/llvm/utils/TableGen/Common/CodeGenInstruction.cpp b/llvm/utils/TableGen/Common/CodeGenInstruction.cpp
index 452b084aa6f7d5..a8ae77bcca3efb 100644
--- a/llvm/utils/TableGen/Common/CodeGenInstruction.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenInstruction.cpp
@@ -469,6 +469,7 @@ CodeGenInstruction::CodeGenInstruction(const Record *R)
FastISelShouldIgnore = R->getValueAsBit("FastISelShouldIgnore");
variadicOpsAreDefs = R->getValueAsBit("variadicOpsAreDefs");
isAuthenticated = R->getValueAsBit("isAuthenticated");
+ isPhi = R->getValueAsBit("isPhi");
bool Unset;
mayLoad = R->getValueAsBitOrUnset("mayLoad", Unset);
diff --git a/llvm/utils/TableGen/Common/CodeGenInstruction.h b/llvm/utils/TableGen/Common/CodeGenInstruction.h
index 18294b157fedb1..c6374ba3366ff7 100644
--- a/llvm/utils/TableGen/Common/CodeGenInstruction.h
+++ b/llvm/utils/TableGen/Common/CodeGenInstruction.h
@@ -286,6 +286,7 @@ class CodeGenInstruction {
bool hasChain_Inferred : 1;
bool variadicOpsAreDefs : 1;
bool isAuthenticated : 1;
+ bool isPhi : 1;
std::string DeprecatedReason;
bool HasComplexDeprecationPredicate;
diff --git a/llvm/utils/TableGen/InstrInfoEmitter.cpp b/llvm/utils/TableGen/InstrInfoEmitter.cpp
index 46605095ba85f8..3bd4d051692b04 100644
--- a/llvm/utils/TableGen/InstrInfoEmitter.cpp
+++ b/llvm/utils/TableGen/InstrInfoEmitter.cpp
@@ -1240,6 +1240,8 @@ void InstrInfoEmitter::emitRecord(
OS << "|(1ULL<<MCID::Select)";
if (Inst.isBarrier)
OS << "|(1ULL<<MCID::Barrier)";
+ if (Inst.isPhi)
+ OS << "|(1ULL<<MCID::Phi)";
if (Inst.hasDelaySlot)
OS << "|(1ULL<<MCID::DelaySlot)";
if (Inst.isCall)
|
@llvm/pr-subscribers-tablegen Author: Nathan Gauër (Keenuts) Changesthe isPHI() function relied on the MIR opcode. This was fine as AFAIK no real target has a real PHI instruction. With SPIR-V, this assumption breaks. Since the SPIR-V opcode is specific to the backend, I don't think checking the actual opcode in the MachineVerifier code is correct, so I added a bit in the instruction description, and applied it to the 3 existing PHI instruction I found (G_PHI, PHI, OpPhi). Another different bit is the index of the first BB/reg pair: %res = PHI [reg, BB] The solution I had is to have a function in the MachineInstr returning the start index, allowing the MachineVerifier to work on both formats. Its slightly better, it works, but it's not THAT great. An alternative could be to add the index in the MCInstrDesc, this way, the index bit definition would be in the TD files, closer to the definition. This patch reduces the amount of failling tests with EXPENSIVE_CHECKS from 120 to 113 (All fixed are in the SPIR-V backend). Fixes #108844 Full diff: https://github.com/llvm/llvm-project/pull/110019.diff 9 Files Affected:
diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h
index 76a7b8662bae66..14ad5531f22c87 100644
--- a/llvm/include/llvm/CodeGen/MachineInstr.h
+++ b/llvm/include/llvm/CodeGen/MachineInstr.h
@@ -1394,10 +1394,19 @@ class MachineInstr
return getOpcode() == TargetOpcode::JUMP_TABLE_DEBUG_INFO;
}
- bool isPHI() const {
- return getOpcode() == TargetOpcode::PHI ||
- getOpcode() == TargetOpcode::G_PHI;
+ bool isPHI() const { return getDesc().isPhi(); }
+
+ unsigned getIndexFirstPHIPair() const {
+ assert(isPHI());
+
+ if (getOpcode() == TargetOpcode::G_PHI || getOpcode() == TargetOpcode::PHI)
+ return 1;
+ // The only other instruction marked as PHI node is OpPhi, in the SPIR-V
+ // backend. The only difference is the [reg, BB] pairs starts at index 2,
+ // not 1.
+ return 2;
}
+
bool isKill() const { return getOpcode() == TargetOpcode::KILL; }
bool isImplicitDef() const { return getOpcode()==TargetOpcode::IMPLICIT_DEF; }
bool isInlineAsm() const {
diff --git a/llvm/include/llvm/MC/MCInstrDesc.h b/llvm/include/llvm/MC/MCInstrDesc.h
index ef0b3c0a73992b..3e7786bf2a3d8e 100644
--- a/llvm/include/llvm/MC/MCInstrDesc.h
+++ b/llvm/include/llvm/MC/MCInstrDesc.h
@@ -187,6 +187,7 @@ enum Flag {
Trap,
VariadicOpsAreDefs,
Authenticated,
+ Phi,
};
} // namespace MCID
@@ -292,6 +293,10 @@ class MCInstrDesc {
/// unconditional branches and return instructions.
bool isBarrier() const { return Flags & (1ULL << MCID::Barrier); }
+ /// Returns true if this instruction acts as a PHI node.
+ /// Not all PHI operands need to dominates the definition.
+ bool isPhi() const { return Flags & (1ULL << MCID::Phi); }
+
/// Returns true if this instruction part of the terminator for
/// a basic block. Typically this is things like return and branch
/// instructions.
diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td
index f5e62dda6fd043..b8a4b9bba3cf67 100644
--- a/llvm/include/llvm/Target/GenericOpcodes.td
+++ b/llvm/include/llvm/Target/GenericOpcodes.td
@@ -96,6 +96,7 @@ def G_PHI : GenericInstruction {
let OutOperandList = (outs type0:$dst);
let InOperandList = (ins variable_ops);
let hasSideEffects = false;
+ let isPhi = true;
}
def G_FRAME_INDEX : GenericInstruction {
diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td
index 3e037affe1cfd2..2b459f0df4228b 100644
--- a/llvm/include/llvm/Target/Target.td
+++ b/llvm/include/llvm/Target/Target.td
@@ -635,6 +635,7 @@ class Instruction : InstructionEncoding {
bit isBitcast = false; // Is this instruction a bitcast instruction?
bit isSelect = false; // Is this instruction a select instruction?
bit isBarrier = false; // Can control flow fall through this instruction?
+ bit isPhi = false; // Is this instruction a phi instruction?
bit isCall = false; // Is this instruction a call instruction?
bit isAdd = false; // Is this instruction an add instruction?
bit isTrap = false; // Is this instruction a trap instruction?
@@ -1174,6 +1175,7 @@ def PHI : StandardPseudoInstruction {
let InOperandList = (ins variable_ops);
let AsmString = "PHINODE";
let hasSideEffects = false;
+ let isPhi = true;
}
def INLINEASM : StandardPseudoInstruction {
let OutOperandList = (outs);
diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index 24a0f41775cc1d..ed843e7a7550f7 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -3213,7 +3213,8 @@ void MachineVerifier::calcRegsRequired() {
// Handle the PHI node.
for (const MachineInstr &MI : MBB.phis()) {
- for (unsigned i = 1, e = MI.getNumOperands(); i != e; i += 2) {
+ for (unsigned i = MI.getIndexFirstPHIPair(), e = MI.getNumOperands();
+ i != e; i += 2) {
// Skip those Operands which are undef regs or not regs.
if (!MI.getOperand(i).isReg() || !MI.getOperand(i).readsReg())
continue;
@@ -3268,7 +3269,8 @@ void MachineVerifier::checkPHIOps(const MachineBasicBlock &MBB) {
if (!DefReg.isVirtual())
report("Expected first PHI operand to be a virtual register", &MODef, 0);
- for (unsigned I = 1, E = Phi.getNumOperands(); I != E; I += 2) {
+ for (unsigned I = Phi.getIndexFirstPHIPair(), E = Phi.getNumOperands();
+ I != E; I += 2) {
const MachineOperand &MO0 = Phi.getOperand(I);
if (!MO0.isReg()) {
report("Expected PHI operand to be a register", &MO0, I);
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
index 51bacb00b1c515..6af0c857b1c582 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
@@ -615,8 +615,10 @@ def OpFwidthCoarse: UnOp<"OpFwidthCoarse", 215>;
// 3.42.17 Control-Flow Instructions
-def OpPhi: Op<245, (outs ID:$res), (ins TYPE:$type, ID:$var0, ID:$block0, variable_ops),
- "$res = OpPhi $type $var0 $block0">;
+let isPhi = 1 in {
+ def OpPhi: Op<245, (outs ID:$res), (ins TYPE:$type, ID:$var0, ID:$block0, variable_ops),
+ "$res = OpPhi $type $var0 $block0">;
+}
def OpLoopMerge: Op<246, (outs), (ins unknown:$merge, unknown:$continue, LoopControl:$lc, variable_ops),
"OpLoopMerge $merge $continue $lc">;
def OpSelectionMerge: Op<247, (outs), (ins unknown:$merge, SelectionControl:$sc),
diff --git a/llvm/utils/TableGen/Common/CodeGenInstruction.cpp b/llvm/utils/TableGen/Common/CodeGenInstruction.cpp
index 452b084aa6f7d5..a8ae77bcca3efb 100644
--- a/llvm/utils/TableGen/Common/CodeGenInstruction.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenInstruction.cpp
@@ -469,6 +469,7 @@ CodeGenInstruction::CodeGenInstruction(const Record *R)
FastISelShouldIgnore = R->getValueAsBit("FastISelShouldIgnore");
variadicOpsAreDefs = R->getValueAsBit("variadicOpsAreDefs");
isAuthenticated = R->getValueAsBit("isAuthenticated");
+ isPhi = R->getValueAsBit("isPhi");
bool Unset;
mayLoad = R->getValueAsBitOrUnset("mayLoad", Unset);
diff --git a/llvm/utils/TableGen/Common/CodeGenInstruction.h b/llvm/utils/TableGen/Common/CodeGenInstruction.h
index 18294b157fedb1..c6374ba3366ff7 100644
--- a/llvm/utils/TableGen/Common/CodeGenInstruction.h
+++ b/llvm/utils/TableGen/Common/CodeGenInstruction.h
@@ -286,6 +286,7 @@ class CodeGenInstruction {
bool hasChain_Inferred : 1;
bool variadicOpsAreDefs : 1;
bool isAuthenticated : 1;
+ bool isPhi : 1;
std::string DeprecatedReason;
bool HasComplexDeprecationPredicate;
diff --git a/llvm/utils/TableGen/InstrInfoEmitter.cpp b/llvm/utils/TableGen/InstrInfoEmitter.cpp
index 46605095ba85f8..3bd4d051692b04 100644
--- a/llvm/utils/TableGen/InstrInfoEmitter.cpp
+++ b/llvm/utils/TableGen/InstrInfoEmitter.cpp
@@ -1240,6 +1240,8 @@ void InstrInfoEmitter::emitRecord(
OS << "|(1ULL<<MCID::Select)";
if (Inst.isBarrier)
OS << "|(1ULL<<MCID::Barrier)";
+ if (Inst.isPhi)
+ OS << "|(1ULL<<MCID::Phi)";
if (Inst.hasDelaySlot)
OS << "|(1ULL<<MCID::DelaySlot)";
if (Inst.isCall)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does PHI end up transforming into an OpPhi? Can you directly consume generic PHI instructions, and only change the final encoding?
@@ -187,6 +187,7 @@ enum Flag { | |||
Trap, | |||
VariadicOpsAreDefs, | |||
Authenticated, | |||
Phi, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These bits are precious and it would be best to avoid spending one on this
@arsenm There is a brief answer and a long one to this very good question. I'd also expect it to be a quite non-trivial task to use generic
I don't say that it's impossible to implement, because it's virtually anything that can be implemented, but it definitely seems unfair to force anyone selecting between being considered invalid and develop something overcomplicated just to avoid such a misjudgment -- and that's actually a part of a longer answer. The point is that GlobalISel has rather poor support for a higher level specification that is SPIR-V. If we narrow things just to MachineVerifier, I can recall one more example, when it's impossible to satisfy MachineVerifier. In I guess this PR is not such a good place for a long reply, but I'd like to provide you with a better context and probably to hear from you an early feedback about the wider problem, even before me and @michalpaszkowski hopefully touch this topic at the 2024 LLVM Developers’ Meeting. |
Thanks @VyacheslavLevytskyy for the detailed reply! I agree, delaying translation would seems weird (the result type, and also... weird to translate all in Isel except OpPhi) After some discussions with a colleague, looks like there is another way which spares this precious bit: adding the info into TargetInstrInfo, and using this to check the opcode/offset. |
Mmm a lot of things are starting to blow up if the OpPhi is generically accepted as a PHI instruction. (MIR optimizations relying on isPHI, but then loading operands by index, hard-coded to start at 1). Surprisingly, this wasn't the case with the change here.
I think option 1 is better, but quite invasive. Option 2 is more safe, localized. |
So, looks like fixing @arsenm : would it be OK to add pointer to TII in |
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 llvm#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 llvm#108844 Signed-off-by: Nathan Gauër <[email protected]>
OK, so I think in this case, it shouldn't be treated like a phi at all. That's structurally different, and should have a different strategy. No generic transform should be treating this as if it were a phi
Not sure what the issue here is, or how it's connected to the phi |
This is legal? "After this, the ‘phi’ instruction takes a list of pairs as arguments, with one pair for each predecessor basic block of the current block. " |
Please have a look at KhronosGroup/SPIRV-LLVM-Translator#2702 for more context, there I've tried to describe it with details. It's exotic but legal. |
Ahh yes, looks like it it, but if the value for the same predecessor is not consistent, then there is a diagnostic. Thanks!
|
I do think it will be easier to maintain generic phi to a later point, not necessarily the very end, than try to pretend your different operation is the same thing. The normal flow is to break phis into copies at the start of register allocation, I would expect the SPIRV analog to introduce whatever flavor it wants around the same point
I think expanding the shape and requirements for what all of codegen must consider as a phi will be more complex |
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 llvm#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 llvm#108844 Signed-off-by: Nathan Gauër <[email protected]>
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 llvm#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 llvm#108844 Signed-off-by: Nathan Gauër <[email protected]>
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 llvm#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 llvm#108844 Signed-off-by: Nathan Gauër <[email protected]>
the isPHI() function relied on the MIR opcode. This was fine as AFAIK no real target has a real PHI instruction. With SPIR-V, this assumption breaks.
The MachineVerifier has a special handling for PHI instructions to check liveness, but since this relied on the PHI/G_PHI opcode check, it was raising an error when the OpPhi MIR was checked.
Since the SPIR-V opcode is specific to the backend, I don't think checking the actual opcode in the MachineVerifier code is correct, so I added a bit in the instruction description, and applied it to the 3 existing PHI instruction I found (G_PHI, PHI, OpPhi).
Another different bit is the index of the first BB/reg pair: %res = PHI [reg, BB]
%res = OpPhi %reg [reg, BB]
The solution I had is to have a function in the MachineInstr returning the start index, allowing the MachineVerifier to work on both formats. Its slightly better, it works, but it's not THAT great. An alternative could be to add the index in the MCInstrDesc, this way, the index bit definition would be in the TD files, closer to the definition.
This patch reduces the amount of failling tests with EXPENSIVE_CHECKS from 120 to 113 (All fixed are in the SPIR-V backend).
Fixes #108844