LLVM 20.0.0git
VPlanVerifier.cpp
Go to the documentation of this file.
1//===-- VPlanVerifier.cpp -------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file defines the class VPlanVerifier, which contains utility functions
11/// to check the consistency and invariants of a VPlan.
12///
13//===----------------------------------------------------------------------===//
14
15#include "VPlanVerifier.h"
16#include "VPlan.h"
17#include "VPlanCFG.h"
18#include "VPlanDominatorTree.h"
20#include "llvm/ADT/TypeSwitch.h"
21
22#define DEBUG_TYPE "loop-vectorize"
23
24using namespace llvm;
25
26namespace {
27class VPlanVerifier {
28 const VPDominatorTree &VPDT;
29
31
32 // Verify that phi-like recipes are at the beginning of \p VPBB, with no
33 // other recipes in between. Also check that only header blocks contain
34 // VPHeaderPHIRecipes.
35 bool verifyPhiRecipes(const VPBasicBlock *VPBB);
36
37 /// Verify that \p EVL is used correctly. The user must be either in
38 /// EVL-based recipes as a last operand or VPInstruction::Add which is
39 /// incoming value into EVL's recipe.
40 bool verifyEVLRecipe(const VPInstruction &EVL) const;
41
42 bool verifyVPBasicBlock(const VPBasicBlock *VPBB);
43
44 bool verifyBlock(const VPBlockBase *VPB);
45
46 /// Helper function that verifies the CFG invariants of the VPBlockBases
47 /// within
48 /// \p Region. Checks in this function are generic for VPBlockBases. They are
49 /// not specific for VPBasicBlocks or VPRegionBlocks.
50 bool verifyBlocksInRegion(const VPRegionBlock *Region);
51
52 /// Verify the CFG invariants of VPRegionBlock \p Region and its nested
53 /// VPBlockBases. Do not recurse inside nested VPRegionBlocks.
54 bool verifyRegion(const VPRegionBlock *Region);
55
56 /// Verify the CFG invariants of VPRegionBlock \p Region and its nested
57 /// VPBlockBases. Recurse inside nested VPRegionBlocks.
58 bool verifyRegionRec(const VPRegionBlock *Region);
59
60public:
61 VPlanVerifier(VPDominatorTree &VPDT) : VPDT(VPDT) {}
62
63 bool verify(const VPlan &Plan);
64};
65} // namespace
66
67bool VPlanVerifier::verifyPhiRecipes(const VPBasicBlock *VPBB) {
68 auto RecipeI = VPBB->begin();
69 auto End = VPBB->end();
70 unsigned NumActiveLaneMaskPhiRecipes = 0;
71 const VPRegionBlock *ParentR = VPBB->getParent();
72 bool IsHeaderVPBB = ParentR && !ParentR->isReplicator() &&
73 ParentR->getEntryBasicBlock() == VPBB;
74 while (RecipeI != End && RecipeI->isPhi()) {
75 if (isa<VPActiveLaneMaskPHIRecipe>(RecipeI))
76 NumActiveLaneMaskPhiRecipes++;
77
78 if (IsHeaderVPBB && !isa<VPHeaderPHIRecipe, VPWidenPHIRecipe>(*RecipeI)) {
79 errs() << "Found non-header PHI recipe in header VPBB";
80#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
81 errs() << ": ";
82 RecipeI->dump();
83#endif
84 return false;
85 }
86
87 if (!IsHeaderVPBB && isa<VPHeaderPHIRecipe>(*RecipeI)) {
88 errs() << "Found header PHI recipe in non-header VPBB";
89#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
90 errs() << ": ";
91 RecipeI->dump();
92#endif
93 return false;
94 }
95
96 RecipeI++;
97 }
98
99 if (NumActiveLaneMaskPhiRecipes > 1) {
100 errs() << "There should be no more than one VPActiveLaneMaskPHIRecipe";
101 return false;
102 }
103
104 while (RecipeI != End) {
105 if (RecipeI->isPhi() && !isa<VPBlendRecipe>(&*RecipeI)) {
106 errs() << "Found phi-like recipe after non-phi recipe";
107
108#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
109 errs() << ": ";
110 RecipeI->dump();
111 errs() << "after\n";
112 std::prev(RecipeI)->dump();
113#endif
114 return false;
115 }
116 RecipeI++;
117 }
118 return true;
119}
120
121bool VPlanVerifier::verifyEVLRecipe(const VPInstruction &EVL) const {
123 errs() << "verifyEVLRecipe should only be called on "
124 "VPInstruction::ExplicitVectorLength\n";
125 return false;
126 }
127 auto VerifyEVLUse = [&](const VPRecipeBase &R,
128 const unsigned ExpectedIdx) -> bool {
129 SmallVector<const VPValue *> Ops(R.operands());
130 unsigned UseCount = count(Ops, &EVL);
131 if (UseCount != 1 || Ops[ExpectedIdx] != &EVL) {
132 errs() << "EVL is used as non-last operand in EVL-based recipe\n";
133 return false;
134 }
135 return true;
136 };
137 return all_of(EVL.users(), [&VerifyEVLUse](VPUser *U) {
138 return TypeSwitch<const VPUser *, bool>(U)
139 .Case<VPWidenIntrinsicRecipe>([&](const VPWidenIntrinsicRecipe *S) {
140 return VerifyEVLUse(*S, S->getNumOperands() - 1);
141 })
143 [&](const VPRecipeBase *S) { return VerifyEVLUse(*S, 2); })
144 .Case<VPWidenLoadEVLRecipe, VPReverseVectorPointerRecipe>(
145 [&](const VPRecipeBase *R) { return VerifyEVLUse(*R, 1); })
146 .Case<VPWidenEVLRecipe>([&](const VPWidenEVLRecipe *W) {
147 return VerifyEVLUse(*W,
148 Instruction::isUnaryOp(W->getOpcode()) ? 1 : 2);
149 })
150 .Case<VPScalarCastRecipe>(
151 [&](const VPScalarCastRecipe *S) { return VerifyEVLUse(*S, 0); })
152 .Case<VPInstruction>([&](const VPInstruction *I) {
153 if (I->getOpcode() != Instruction::Add) {
154 errs() << "EVL is used as an operand in non-VPInstruction::Add\n";
155 return false;
156 }
157 if (I->getNumUsers() != 1) {
158 errs() << "EVL is used in VPInstruction:Add with multiple "
159 "users\n";
160 return false;
161 }
162 if (!isa<VPEVLBasedIVPHIRecipe>(*I->users().begin())) {
163 errs() << "Result of VPInstruction::Add with EVL operand is "
164 "not used by VPEVLBasedIVPHIRecipe\n";
165 return false;
166 }
167 return true;
168 })
169 .Default([&](const VPUser *U) {
170 errs() << "EVL has unexpected user\n";
171 return false;
172 });
173 });
174}
175
176bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) {
177 if (!verifyPhiRecipes(VPBB))
178 return false;
179
180 // Verify that defs in VPBB dominate all their uses. The current
181 // implementation is still incomplete.
183 unsigned Cnt = 0;
184 for (const VPRecipeBase &R : *VPBB)
185 RecipeNumbering[&R] = Cnt++;
186
187 for (const VPRecipeBase &R : *VPBB) {
188 if (isa<VPIRInstruction>(&R) && !isa<VPIRBasicBlock>(VPBB)) {
189 errs() << "VPIRInstructions ";
190#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
191 R.dump();
192 errs() << " ";
193#endif
194 errs() << "not in a VPIRBasicBlock!\n";
195 return false;
196 }
197 for (const VPValue *V : R.definedValues()) {
198 for (const VPUser *U : V->users()) {
199 auto *UI = dyn_cast<VPRecipeBase>(U);
200 // TODO: check dominance of incoming values for phis properly.
201 if (!UI ||
202 isa<VPHeaderPHIRecipe, VPWidenPHIRecipe, VPPredInstPHIRecipe>(UI))
203 continue;
204
205 // If the user is in the same block, check it comes after R in the
206 // block.
207 if (UI->getParent() == VPBB) {
208 if (RecipeNumbering[UI] < RecipeNumbering[&R]) {
209 errs() << "Use before def!\n";
210 return false;
211 }
212 continue;
213 }
214
215 if (!VPDT.dominates(VPBB, UI->getParent())) {
216 errs() << "Use before def!\n";
217 return false;
218 }
219 }
220 }
221 if (const auto *EVL = dyn_cast<VPInstruction>(&R)) {
223 !verifyEVLRecipe(*EVL)) {
224 errs() << "EVL VPValue is not used correctly\n";
225 return false;
226 }
227 }
228 }
229
230 auto *IRBB = dyn_cast<VPIRBasicBlock>(VPBB);
231 if (!IRBB)
232 return true;
233
234 if (!WrappedIRBBs.insert(IRBB->getIRBasicBlock()).second) {
235 errs() << "Same IR basic block used by multiple wrapper blocks!\n";
236 return false;
237 }
238
239 return true;
240}
241
242/// Utility function that checks whether \p VPBlockVec has duplicate
243/// VPBlockBases.
244static bool hasDuplicates(const SmallVectorImpl<VPBlockBase *> &VPBlockVec) {
246 for (const auto *Block : VPBlockVec) {
247 if (!VPBlockSet.insert(Block).second)
248 return true;
249 }
250 return false;
251}
252
253bool VPlanVerifier::verifyBlock(const VPBlockBase *VPB) {
254 auto *VPBB = dyn_cast<VPBasicBlock>(VPB);
255 // Check block's condition bit.
256 if (VPB->getNumSuccessors() > 1 ||
257 (VPBB && VPBB->getParent() && VPBB->isExiting() &&
258 !VPBB->getParent()->isReplicator())) {
259 if (!VPBB || !VPBB->getTerminator()) {
260 errs() << "Block has multiple successors but doesn't "
261 "have a proper branch recipe!\n";
262 return false;
263 }
264 } else {
265 if (VPBB && VPBB->getTerminator()) {
266 errs() << "Unexpected branch recipe!\n";
267 return false;
268 }
269 }
270
271 // Check block's successors.
272 const auto &Successors = VPB->getSuccessors();
273 // There must be only one instance of a successor in block's successor list.
274 // TODO: This won't work for switch statements.
275 if (hasDuplicates(Successors)) {
276 errs() << "Multiple instances of the same successor.\n";
277 return false;
278 }
279
280 for (const VPBlockBase *Succ : Successors) {
281 // There must be a bi-directional link between block and successor.
282 const auto &SuccPreds = Succ->getPredecessors();
283 if (!is_contained(SuccPreds, VPB)) {
284 errs() << "Missing predecessor link.\n";
285 return false;
286 }
287 }
288
289 // Check block's predecessors.
290 const auto &Predecessors = VPB->getPredecessors();
291 // There must be only one instance of a predecessor in block's predecessor
292 // list.
293 // TODO: This won't work for switch statements.
294 if (hasDuplicates(Predecessors)) {
295 errs() << "Multiple instances of the same predecessor.\n";
296 return false;
297 }
298
299 for (const VPBlockBase *Pred : Predecessors) {
300 // Block and predecessor must be inside the same region.
301 if (Pred->getParent() != VPB->getParent()) {
302 errs() << "Predecessor is not in the same region.\n";
303 return false;
304 }
305
306 // There must be a bi-directional link between block and predecessor.
307 const auto &PredSuccs = Pred->getSuccessors();
308 if (!is_contained(PredSuccs, VPB)) {
309 errs() << "Missing successor link.\n";
310 return false;
311 }
312 }
313 return !VPBB || verifyVPBasicBlock(VPBB);
314}
315
316bool VPlanVerifier::verifyBlocksInRegion(const VPRegionBlock *Region) {
317 for (const VPBlockBase *VPB : vp_depth_first_shallow(Region->getEntry())) {
318 // Check block's parent.
319 if (VPB->getParent() != Region) {
320 errs() << "VPBlockBase has wrong parent\n";
321 return false;
322 }
323
324 if (!verifyBlock(VPB))
325 return false;
326 }
327 return true;
328}
329
330bool VPlanVerifier::verifyRegion(const VPRegionBlock *Region) {
331 const VPBlockBase *Entry = Region->getEntry();
332 const VPBlockBase *Exiting = Region->getExiting();
333
334 // Entry and Exiting shouldn't have any predecessor/successor, respectively.
335 if (Entry->getNumPredecessors() != 0) {
336 errs() << "region entry block has predecessors\n";
337 return false;
338 }
339 if (Exiting->getNumSuccessors() != 0) {
340 errs() << "region exiting block has successors\n";
341 return false;
342 }
343
344 return verifyBlocksInRegion(Region);
345}
346
347bool VPlanVerifier::verifyRegionRec(const VPRegionBlock *Region) {
348 // Recurse inside nested regions and check all blocks inside the region.
349 return verifyRegion(Region) &&
351 [this](const VPBlockBase *VPB) {
352 const auto *SubRegion = dyn_cast<VPRegionBlock>(VPB);
353 return !SubRegion || verifyRegionRec(SubRegion);
354 });
355}
356
357bool VPlanVerifier::verify(const VPlan &Plan) {
359 [this](const VPBlockBase *VPB) { return !verifyBlock(VPB); }))
360 return false;
361
362 const VPRegionBlock *TopRegion = Plan.getVectorLoopRegion();
363 if (!verifyRegionRec(TopRegion))
364 return false;
365
366 if (TopRegion->getParent()) {
367 errs() << "VPlan Top Region should have no parent.\n";
368 return false;
369 }
370
371 const VPBasicBlock *Entry = dyn_cast<VPBasicBlock>(TopRegion->getEntry());
372 if (!Entry) {
373 errs() << "VPlan entry block is not a VPBasicBlock\n";
374 return false;
375 }
376
377 if (!isa<VPCanonicalIVPHIRecipe>(&*Entry->begin())) {
378 errs() << "VPlan vector loop header does not start with a "
379 "VPCanonicalIVPHIRecipe\n";
380 return false;
381 }
382
383 const VPBasicBlock *Exiting = dyn_cast<VPBasicBlock>(TopRegion->getExiting());
384 if (!Exiting) {
385 errs() << "VPlan exiting block is not a VPBasicBlock\n";
386 return false;
387 }
388
389 if (Exiting->empty()) {
390 errs() << "VPlan vector loop exiting block must end with BranchOnCount or "
391 "BranchOnCond VPInstruction but is empty\n";
392 return false;
393 }
394
395 auto *LastInst = dyn_cast<VPInstruction>(std::prev(Exiting->end()));
396 if (!LastInst || (LastInst->getOpcode() != VPInstruction::BranchOnCount &&
397 LastInst->getOpcode() != VPInstruction::BranchOnCond)) {
398 errs() << "VPlan vector loop exit must end with BranchOnCount or "
399 "BranchOnCond VPInstruction\n";
400 return false;
401 }
402
403 return true;
404}
405
407 VPDominatorTree VPDT;
408 VPDT.recalculate(const_cast<VPlan &>(Plan));
409 VPlanVerifier Verifier(VPDT);
410 return Verifier.verify(Plan);
411}
@ Default
Definition: DwarfDebug.cpp:87
bool End
Definition: ELF_riscv.cpp:480
#define I(x, y, z)
Definition: MD5.cpp:58
ppc ctr loops verify
verify safepoint Safepoint IR Verifier
This file defines the SmallPtrSet class.
This file implements the TypeSwitch template, which mimics a switch() statement whose cases are type ...
This file implements dominator tree analysis for a single level of a VPlan's H-CFG.
static bool hasDuplicates(const SmallVectorImpl< VPBlockBase * > &VPBlockVec)
Utility function that checks whether VPBlockVec has duplicate VPBlockBases.
This file declares the class VPlanVerifier, which contains utility functions to check the consistency...
This file contains the declarations of the Vectorization Plan base classes:
void recalculate(ParentType &Func)
recalculate - compute a dominator tree for the given function
bool isUnaryOp() const
Definition: Instruction.h:278
BlockT * getEntry() const
Get the entry BasicBlock of the Region.
Definition: RegionInfo.h:320
Implements a dense probed hash-table based set with some number of buckets stored inline.
Definition: DenseSet.h:298
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
Definition: SmallPtrSet.h:519
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:573
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1196
VPBasicBlock serves as the leaf of the Hierarchical Control-Flow Graph.
Definition: VPlan.h:3470
iterator end()
Definition: VPlan.h:3504
iterator begin()
Recipe iterator methods.
Definition: VPlan.h:3502
bool empty() const
Definition: VPlan.h:3513
VPBlockBase is the building block of the Hierarchical Control-Flow Graph.
Definition: VPlan.h:396
VPRegionBlock * getParent()
Definition: VPlan.h:488
size_t getNumSuccessors() const
Definition: VPlan.h:534
const VPBlocksTy & getPredecessors() const
Definition: VPlan.h:519
const VPBasicBlock * getEntryBasicBlock() const
Definition: VPlan.cpp:158
const VPBlocksTy & getSuccessors() const
Definition: VPlan.h:513
Template specialization of the standard LLVM dominator tree utility for VPBlockBases.
This is a concrete Recipe that models a single VPlan-level instruction.
Definition: VPlan.h:1197
unsigned getOpcode() const
Definition: VPlan.h:1315
VPRecipeBase is a base class modeling a sequence of one or more output IR instructions.
Definition: VPlan.h:720
A recipe to represent inloop reduction operations with vector-predication intrinsics,...
Definition: VPlan.h:2665
VPRegionBlock represents a collection of VPBasicBlocks and VPRegionBlocks which form a Single-Entry-S...
Definition: VPlan.h:3657
const VPBlockBase * getEntry() const
Definition: VPlan.h:3696
bool isReplicator() const
An indicator whether this region is to generate multiple replicated instances of output IR correspond...
Definition: VPlan.h:3728
const VPBlockBase * getExiting() const
Definition: VPlan.h:3708
VPScalarCastRecipe is a recipe to create scalar cast instructions.
Definition: VPlan.h:1581
This class augments VPValue with operands which provide the inverse def-use edges from VPValue's user...
Definition: VPlanValue.h:200
user_range users()
Definition: VPlanValue.h:132
A recipe for widening operations with vector-predication intrinsics with explicit vector length (EVL)...
Definition: VPlan.h:1482
VPlan models a candidate for vectorization, encoding various decisions take to produce efficient outp...
Definition: VPlan.h:3761
VPBasicBlock * getEntry()
Definition: VPlan.h:3869
VPRegionBlock * getVectorLoopRegion()
Returns the VPRegionBlock of the vector loop.
Definition: VPlan.cpp:1084
std::pair< iterator, bool > insert(const ValueT &V)
Definition: DenseSet.h:213
@ Entry
Definition: COFF.h:844
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1739
iterator_range< df_iterator< VPBlockShallowTraversalWrapper< VPBlockBase * > > > vp_depth_first_shallow(VPBlockBase *G)
Returns an iterator range to traverse the graph starting at G in depth-first order.
Definition: VPlanCFG.h:214
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1746
raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
auto count(R &&Range, const E &Element)
Wrapper function around std::count to count the number of times an element Element occurs in the give...
Definition: STLExtras.h:1938
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition: STLExtras.h:1903
bool verifyVPlanIsValid(const VPlan &Plan)
Verify invariants for general VPlans.
A recipe for widening store operations with vector-predication intrinsics, using the value to store,...
Definition: VPlan.h:3078