Skip to content

Commit 7b75db5

Browse files
authored
[VPlan] Add new VPIRPhi overlay for VPIRInsts wrapping phi nodes (NFC). (#129387)
Add a new VPIRPhi subclass of VPIRInstruction, that purely serves as an overlay, to provide more convenient checking (via directly doing isa/dyn_cast/cast) and specialied execute/print implementations. Both VPIRInstruction and VPIRPhi share the same VPDefID, and are differentiated by the backing IR instruction. This pattern could alos be used to provide more specialized interfaces for some VPInstructions ocpodes, without introducing new, completely spearate recipes. An example would be modeling VPWidenPHIRecipe & VPScalarPHIRecip using VPInstructions opcodes and providing an interface to retrieve incoming blocks and values through a VPInstruction subclass similar to VPIRPhi. PR: #129387
1 parent 8836128 commit 7b75db5

File tree

6 files changed

+96
-51
lines changed

6 files changed

+96
-51
lines changed

llvm/lib/Transforms/Vectorize/LoopVectorize.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9079,14 +9079,14 @@ static void addScalarResumePhis(VPRecipeBuilder &Builder, VPlan &Plan,
90799079
VPValue *OneVPV = Plan.getOrAddLiveIn(
90809080
ConstantInt::get(Plan.getCanonicalIV()->getScalarType(), 1));
90819081
for (VPRecipeBase &ScalarPhiR : *Plan.getScalarHeader()) {
9082-
auto *ScalarPhiIRI = cast<VPIRInstruction>(&ScalarPhiR);
9083-
auto *ScalarPhiI = dyn_cast<PHINode>(&ScalarPhiIRI->getInstruction());
9084-
if (!ScalarPhiI)
9082+
auto *ScalarPhiIRI = dyn_cast<VPIRPhi>(&ScalarPhiR);
9083+
if (!ScalarPhiIRI)
90859084
break;
90869085

90879086
// TODO: Extract final value from induction recipe initially, optimize to
90889087
// pre-computed end value together in optimizeInductionExitUsers.
9089-
auto *VectorPhiR = cast<VPHeaderPHIRecipe>(Builder.getRecipe(ScalarPhiI));
9088+
auto *VectorPhiR =
9089+
cast<VPHeaderPHIRecipe>(Builder.getRecipe(&ScalarPhiIRI->getIRPhi()));
90909090
if (auto *WideIVR = dyn_cast<VPWidenInductionRecipe>(VectorPhiR)) {
90919091
if (VPInstruction *ResumePhi = addResumePhiRecipeForInduction(
90929092
WideIVR, VectorPHBuilder, ScalarPHBuilder, TypeInfo,
@@ -9136,20 +9136,19 @@ collectUsersInExitBlocks(Loop *OrigLoop, VPRecipeBuilder &Builder,
91369136
continue;
91379137

91389138
for (VPRecipeBase &R : *ExitVPBB) {
9139-
auto *ExitIRI = dyn_cast<VPIRInstruction>(&R);
9139+
auto *ExitIRI = dyn_cast<VPIRPhi>(&R);
91409140
if (!ExitIRI)
9141-
continue;
9142-
auto *ExitPhi = dyn_cast<PHINode>(&ExitIRI->getInstruction());
9143-
if (!ExitPhi)
91449141
break;
91459142
if (ExitVPBB->getSinglePredecessor() != Plan.getMiddleBlock()) {
91469143
assert(ExitIRI->getNumOperands() ==
91479144
ExitVPBB->getPredecessors().size() &&
91489145
"early-exit must update exit values on construction");
91499146
continue;
91509147
}
9148+
9149+
PHINode &ExitPhi = ExitIRI->getIRPhi();
91519150
BasicBlock *ExitingBB = OrigLoop->getLoopLatch();
9152-
Value *IncomingValue = ExitPhi->getIncomingValueForBlock(ExitingBB);
9151+
Value *IncomingValue = ExitPhi.getIncomingValueForBlock(ExitingBB);
91539152
VPValue *V = Builder.getVPValueOrAddLiveIn(IncomingValue);
91549153
ExitIRI->addOperand(V);
91559154
if (V->isLiveIn())
@@ -10347,11 +10346,10 @@ static void preparePlanForMainVectorLoop(VPlan &MainPlan, VPlan &EpiPlan) {
1034710346
cast<PHINode>(R.getVPSingleValue()->getUnderlyingValue()));
1034810347
}
1034910348
for (VPRecipeBase &R : make_early_inc_range(*MainPlan.getScalarHeader())) {
10350-
auto *VPIRInst = cast<VPIRInstruction>(&R);
10351-
auto *IRI = dyn_cast<PHINode>(&VPIRInst->getInstruction());
10352-
if (!IRI)
10349+
auto *VPIRInst = dyn_cast<VPIRPhi>(&R);
10350+
if (!VPIRInst)
1035310351
break;
10354-
if (EpiWidenedPhis.contains(IRI))
10352+
if (EpiWidenedPhis.contains(&VPIRInst->getIRPhi()))
1035510353
continue;
1035610354
// There is no corresponding wide induction in the epilogue plan that would
1035710355
// need a resume value. Remove the VPIRInst wrapping the scalar header phi

llvm/lib/Transforms/Vectorize/VPlan.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1225,7 +1225,7 @@ VPIRBasicBlock *VPlan::createVPIRBasicBlock(BasicBlock *IRBB) {
12251225
auto *VPIRBB = createEmptyVPIRBasicBlock(IRBB);
12261226
for (Instruction &I :
12271227
make_range(IRBB->begin(), IRBB->getTerminator()->getIterator()))
1228-
VPIRBB->appendRecipe(new VPIRInstruction(I));
1228+
VPIRBB->appendRecipe(VPIRInstruction::create(I));
12291229
return VPIRBB;
12301230
}
12311231

llvm/lib/Transforms/Vectorize/VPlan.h

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,22 +1026,28 @@ class VPInstruction : public VPRecipeWithIRFlags,
10261026
};
10271027

10281028
/// A recipe to wrap on original IR instruction not to be modified during
1029-
/// execution, execept for PHIs. For PHIs, a single VPValue operand is allowed,
1030-
/// and it is used to add a new incoming value for the single predecessor VPBB.
1029+
/// execution, except for PHIs. PHIs are modeled via the VPIRPhi subclass.
10311030
/// Expect PHIs, VPIRInstructions cannot have any operands.
10321031
class VPIRInstruction : public VPRecipeBase {
10331032
Instruction &I;
10341033

1035-
public:
1034+
protected:
1035+
/// VPIRInstruction::create() should be used to create VPIRInstructions, as
1036+
/// subclasses may need to be created, e.g. VPIRPhi.
10361037
VPIRInstruction(Instruction &I)
10371038
: VPRecipeBase(VPDef::VPIRInstructionSC, ArrayRef<VPValue *>()), I(I) {}
10381039

1040+
public:
10391041
~VPIRInstruction() override = default;
10401042

1043+
/// Create a new VPIRPhi for \p \I, if it is a PHINode, otherwise create a
1044+
/// VPIRInstruction.
1045+
static VPIRInstruction *create(Instruction &I);
1046+
10411047
VP_CLASSOF_IMPL(VPDef::VPIRInstructionSC)
10421048

10431049
VPIRInstruction *clone() override {
1044-
auto *R = new VPIRInstruction(I);
1050+
auto *R = create(I);
10451051
for (auto *Op : operands())
10461052
R->addOperand(Op);
10471053
return R;
@@ -1085,6 +1091,29 @@ class VPIRInstruction : public VPRecipeBase {
10851091
void extractLastLaneOfOperand(VPBuilder &Builder);
10861092
};
10871093

1094+
/// An overlay for VPIRInstructions wrapping PHI nodes enabling convenient use
1095+
/// cast/dyn_cast/isa and execute() implementation. A single VPValue operand is
1096+
/// allowed, and it is used to add a new incoming value for the single
1097+
/// predecessor VPBB.
1098+
struct VPIRPhi : public VPIRInstruction {
1099+
VPIRPhi(PHINode &PN) : VPIRInstruction(PN) {}
1100+
1101+
static inline bool classof(const VPRecipeBase *U) {
1102+
auto *R = dyn_cast<VPIRInstruction>(U);
1103+
return R && isa<PHINode>(R->getInstruction());
1104+
}
1105+
1106+
PHINode &getIRPhi() { return cast<PHINode>(getInstruction()); }
1107+
1108+
void execute(VPTransformState &State) override;
1109+
1110+
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
1111+
/// Print the recipe.
1112+
void print(raw_ostream &O, const Twine &Indent,
1113+
VPSlotTracker &SlotTracker) const override;
1114+
#endif
1115+
};
1116+
10881117
/// VPWidenRecipe is a recipe for producing a widened instruction using the
10891118
/// opcode and operands of the recipe. This recipe covers most of the
10901119
/// traditional vectorization cases where each recipe transforms into a

llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,30 +1064,15 @@ void VPInstruction::print(raw_ostream &O, const Twine &Indent,
10641064
}
10651065
#endif
10661066

1067-
void VPIRInstruction::execute(VPTransformState &State) {
1068-
assert((isa<PHINode>(&I) || getNumOperands() == 0) &&
1069-
"Only PHINodes can have extra operands");
1070-
for (const auto &[Idx, Op] : enumerate(operands())) {
1071-
VPValue *ExitValue = Op;
1072-
auto Lane = vputils::isUniformAfterVectorization(ExitValue)
1073-
? VPLane::getFirstLane()
1074-
: VPLane::getLastLaneForVF(State.VF);
1075-
VPBlockBase *Pred = getParent()->getPredecessors()[Idx];
1076-
auto *PredVPBB = Pred->getExitingBasicBlock();
1077-
BasicBlock *PredBB = State.CFG.VPBB2IRBB[PredVPBB];
1078-
// Set insertion point in PredBB in case an extract needs to be generated.
1079-
// TODO: Model extracts explicitly.
1080-
State.Builder.SetInsertPoint(PredBB, PredBB->getFirstNonPHIIt());
1081-
Value *V = State.get(ExitValue, VPLane(Lane));
1082-
auto *Phi = cast<PHINode>(&I);
1083-
// If there is no existing block for PredBB in the phi, add a new incoming
1084-
// value. Otherwise update the existing incoming value for PredBB.
1085-
if (Phi->getBasicBlockIndex(PredBB) == -1)
1086-
Phi->addIncoming(V, PredBB);
1087-
else
1088-
Phi->setIncomingValueForBlock(PredBB, V);
1089-
}
1067+
VPIRInstruction *VPIRInstruction ::create(Instruction &I) {
1068+
if (auto *Phi = dyn_cast<PHINode>(&I))
1069+
return new VPIRPhi(*Phi);
1070+
return new VPIRInstruction(I);
1071+
}
10901072

1073+
void VPIRInstruction::execute(VPTransformState &State) {
1074+
assert(!isa<VPIRPhi>(this) && getNumOperands() == 0 &&
1075+
"PHINodes must be handled by VPIRPhi");
10911076
// Advance the insert point after the wrapped IR instruction. This allows
10921077
// interleaving VPIRInstructions and other recipes.
10931078
State.Builder.SetInsertPoint(I.getParent(), std::next(I.getIterator()));
@@ -1120,6 +1105,40 @@ void VPIRInstruction::extractLastLaneOfOperand(VPBuilder &Builder) {
11201105
void VPIRInstruction::print(raw_ostream &O, const Twine &Indent,
11211106
VPSlotTracker &SlotTracker) const {
11221107
O << Indent << "IR " << I;
1108+
}
1109+
#endif
1110+
1111+
void VPIRPhi::execute(VPTransformState &State) {
1112+
PHINode *Phi = &getIRPhi();
1113+
for (const auto &[Idx, Op] : enumerate(operands())) {
1114+
VPValue *ExitValue = Op;
1115+
auto Lane = vputils::isUniformAfterVectorization(ExitValue)
1116+
? VPLane::getFirstLane()
1117+
: VPLane::getLastLaneForVF(State.VF);
1118+
VPBlockBase *Pred = getParent()->getPredecessors()[Idx];
1119+
auto *PredVPBB = Pred->getExitingBasicBlock();
1120+
BasicBlock *PredBB = State.CFG.VPBB2IRBB[PredVPBB];
1121+
// Set insertion point in PredBB in case an extract needs to be generated.
1122+
// TODO: Model extracts explicitly.
1123+
State.Builder.SetInsertPoint(PredBB, PredBB->getFirstNonPHIIt());
1124+
Value *V = State.get(ExitValue, VPLane(Lane));
1125+
// If there is no existing block for PredBB in the phi, add a new incoming
1126+
// value. Otherwise update the existing incoming value for PredBB.
1127+
if (Phi->getBasicBlockIndex(PredBB) == -1)
1128+
Phi->addIncoming(V, PredBB);
1129+
else
1130+
Phi->setIncomingValueForBlock(PredBB, V);
1131+
}
1132+
1133+
// Advance the insert point after the wrapped IR instruction. This allows
1134+
// interleaving VPIRInstructions and other recipes.
1135+
State.Builder.SetInsertPoint(Phi->getParent(), std::next(Phi->getIterator()));
1136+
}
1137+
1138+
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
1139+
void VPIRPhi::print(raw_ostream &O, const Twine &Indent,
1140+
VPSlotTracker &SlotTracker) const {
1141+
VPIRInstruction::print(O, Indent, SlotTracker);
11231142

11241143
if (getNumOperands() != 0) {
11251144
O << " (extra operand" << (getNumOperands() > 1 ? "s" : "") << ": ";

llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -861,8 +861,8 @@ void VPlanTransforms::optimizeInductionExitUsers(
861861
VPTypeAnalysis TypeInfo(Plan.getCanonicalIV()->getScalarType());
862862
for (VPIRBasicBlock *ExitVPBB : Plan.getExitBlocks()) {
863863
for (VPRecipeBase &R : *ExitVPBB) {
864-
auto *ExitIRI = cast<VPIRInstruction>(&R);
865-
if (!isa<PHINode>(ExitIRI->getInstruction()))
864+
auto *ExitIRI = dyn_cast<VPIRPhi>(&R);
865+
if (!ExitIRI)
866866
break;
867867

868868
for (auto [Idx, PredVPBB] : enumerate(ExitVPBB->getPredecessors())) {
@@ -2221,20 +2221,20 @@ void VPlanTransforms::handleUncountableEarlyExit(
22212221
VPBuilder MiddleBuilder(NewMiddle);
22222222
VPBuilder EarlyExitB(VectorEarlyExitVPBB);
22232223
for (VPRecipeBase &R : *VPEarlyExitBlock) {
2224-
auto *ExitIRI = cast<VPIRInstruction>(&R);
2225-
auto *ExitPhi = dyn_cast<PHINode>(&ExitIRI->getInstruction());
2226-
if (!ExitPhi)
2224+
auto *ExitIRI = dyn_cast<VPIRPhi>(&R);
2225+
if (!ExitIRI)
22272226
break;
22282227

2228+
PHINode &ExitPhi = ExitIRI->getIRPhi();
22292229
VPValue *IncomingFromEarlyExit = RecipeBuilder.getVPValueOrAddLiveIn(
2230-
ExitPhi->getIncomingValueForBlock(UncountableExitingBlock));
2230+
ExitPhi.getIncomingValueForBlock(UncountableExitingBlock));
22312231

22322232
if (OrigLoop->getUniqueExitBlock()) {
22332233
// If there's a unique exit block, VPEarlyExitBlock has 2 predecessors
22342234
// (MiddleVPBB and NewMiddle). Add the incoming value from MiddleVPBB
22352235
// which is coming from the original latch.
22362236
VPValue *IncomingFromLatch = RecipeBuilder.getVPValueOrAddLiveIn(
2237-
ExitPhi->getIncomingValueForBlock(OrigLoop->getLoopLatch()));
2237+
ExitPhi.getIncomingValueForBlock(OrigLoop->getLoopLatch()));
22382238
ExitIRI->addOperand(IncomingFromLatch);
22392239
ExitIRI->extractLastLaneOfOperand(MiddleBuilder);
22402240
}

llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,8 @@ bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) {
209209
auto *UI = cast<VPRecipeBase>(U);
210210
// TODO: check dominance of incoming values for phis properly.
211211
if (!UI ||
212-
isa<VPHeaderPHIRecipe, VPWidenPHIRecipe, VPPredInstPHIRecipe>(UI) ||
213-
(isa<VPIRInstruction>(UI) &&
214-
isa<PHINode>(cast<VPIRInstruction>(UI)->getInstruction())) ||
212+
isa<VPHeaderPHIRecipe, VPWidenPHIRecipe, VPPredInstPHIRecipe,
213+
VPIRPhi>(UI) ||
215214
(isa<VPInstruction>(UI) &&
216215
cast<VPInstruction>(UI)->getOpcode() == Instruction::PHI))
217216
continue;

0 commit comments

Comments
 (0)