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 VPTypeAnalysis &TypeInfo;
30
32
33 // Verify that phi-like recipes are at the beginning of \p VPBB, with no
34 // other recipes in between. Also check that only header blocks contain
35 // VPHeaderPHIRecipes.
36 bool verifyPhiRecipes(const VPBasicBlock *VPBB);
37
38 /// Verify that \p EVL is used correctly. The user must be either in
39 /// EVL-based recipes as a last operand or VPInstruction::Add which is
40 /// incoming value into EVL's recipe.
41 bool verifyEVLRecipe(const VPInstruction &EVL) const;
42
43 bool verifyVPBasicBlock(const VPBasicBlock *VPBB);
44
45 bool verifyBlock(const VPBlockBase *VPB);
46
47 /// Helper function that verifies the CFG invariants of the VPBlockBases
48 /// within
49 /// \p Region. Checks in this function are generic for VPBlockBases. They are
50 /// not specific for VPBasicBlocks or VPRegionBlocks.
51 bool verifyBlocksInRegion(const VPRegionBlock *Region);
52
53 /// Verify the CFG invariants of VPRegionBlock \p Region and its nested
54 /// VPBlockBases. Do not recurse inside nested VPRegionBlocks.
55 bool verifyRegion(const VPRegionBlock *Region);
56
57 /// Verify the CFG invariants of VPRegionBlock \p Region and its nested
58 /// VPBlockBases. Recurse inside nested VPRegionBlocks.
59 bool verifyRegionRec(const VPRegionBlock *Region);
60
61public:
62 VPlanVerifier(VPDominatorTree &VPDT, VPTypeAnalysis &TypeInfo)
63 : VPDT(VPDT), TypeInfo(TypeInfo) {}
64
65 bool verify(const VPlan &Plan);
66};
67} // namespace
68
69bool VPlanVerifier::verifyPhiRecipes(const VPBasicBlock *VPBB) {
70 auto RecipeI = VPBB->begin();
71 auto End = VPBB->end();
72 unsigned NumActiveLaneMaskPhiRecipes = 0;
73 const VPRegionBlock *ParentR = VPBB->getParent();
74 bool IsHeaderVPBB = ParentR && !ParentR->isReplicator() &&
75 ParentR->getEntryBasicBlock() == VPBB;
76 while (RecipeI != End && RecipeI->isPhi()) {
77 if (isa<VPActiveLaneMaskPHIRecipe>(RecipeI))
78 NumActiveLaneMaskPhiRecipes++;
79
80 if (IsHeaderVPBB && !isa<VPHeaderPHIRecipe, VPWidenPHIRecipe>(*RecipeI)) {
81 errs() << "Found non-header PHI recipe in header VPBB";
82#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
83 errs() << ": ";
84 RecipeI->dump();
85#endif
86 return false;
87 }
88
89 if (!IsHeaderVPBB && isa<VPHeaderPHIRecipe>(*RecipeI)) {
90 errs() << "Found header PHI recipe in non-header VPBB";
91#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
92 errs() << ": ";
93 RecipeI->dump();
94#endif
95 return false;
96 }
97
98 RecipeI++;
99 }
100
101 if (NumActiveLaneMaskPhiRecipes > 1) {
102 errs() << "There should be no more than one VPActiveLaneMaskPHIRecipe";
103 return false;
104 }
105
106 while (RecipeI != End) {
107 if (RecipeI->isPhi() && !isa<VPBlendRecipe>(&*RecipeI)) {
108 errs() << "Found phi-like recipe after non-phi recipe";
109
110#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
111 errs() << ": ";
112 RecipeI->dump();
113 errs() << "after\n";
114 std::prev(RecipeI)->dump();
115#endif
116 return false;
117 }
118 RecipeI++;
119 }
120 return true;
121}
122
123bool VPlanVerifier::verifyEVLRecipe(const VPInstruction &EVL) const {
125 errs() << "verifyEVLRecipe should only be called on "
126 "VPInstruction::ExplicitVectorLength\n";
127 return false;
128 }
129 auto VerifyEVLUse = [&](const VPRecipeBase &R,
130 const unsigned ExpectedIdx) -> bool {
131 SmallVector<const VPValue *> Ops(R.operands());
132 unsigned UseCount = count(Ops, &EVL);
133 if (UseCount != 1 || Ops[ExpectedIdx] != &EVL) {
134 errs() << "EVL is used as non-last operand in EVL-based recipe\n";
135 return false;
136 }
137 return true;
138 };
139 return all_of(EVL.users(), [&VerifyEVLUse](VPUser *U) {
140 return TypeSwitch<const VPUser *, bool>(U)
141 .Case<VPWidenIntrinsicRecipe>([&](const VPWidenIntrinsicRecipe *S) {
142 return VerifyEVLUse(*S, S->getNumOperands() - 1);
143 })
145 [&](const VPRecipeBase *S) { return VerifyEVLUse(*S, 2); })
146 .Case<VPWidenLoadEVLRecipe, VPReverseVectorPointerRecipe>(
147 [&](const VPRecipeBase *R) { return VerifyEVLUse(*R, 1); })
148 .Case<VPWidenEVLRecipe>([&](const VPWidenEVLRecipe *W) {
149 return VerifyEVLUse(*W,
150 Instruction::isUnaryOp(W->getOpcode()) ? 1 : 2);
151 })
152 .Case<VPScalarCastRecipe>(
153 [&](const VPScalarCastRecipe *S) { return VerifyEVLUse(*S, 0); })
154 .Case<VPInstruction>([&](const VPInstruction *I) {
155 if (I->getOpcode() != Instruction::Add) {
156 errs() << "EVL is used as an operand in non-VPInstruction::Add\n";
157 return false;
158 }
159 if (I->getNumUsers() != 1) {
160 errs() << "EVL is used in VPInstruction:Add with multiple "
161 "users\n";
162 return false;
163 }
164 if (!isa<VPEVLBasedIVPHIRecipe>(*I->users().begin())) {
165 errs() << "Result of VPInstruction::Add with EVL operand is "
166 "not used by VPEVLBasedIVPHIRecipe\n";
167 return false;
168 }
169 return true;
170 })
171 .Default([&](const VPUser *U) {
172 errs() << "EVL has unexpected user\n";
173 return false;
174 });
175 });
176}
177
178bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) {
179 if (!verifyPhiRecipes(VPBB))
180 return false;
181
182 // Verify that defs in VPBB dominate all their uses. The current
183 // implementation is still incomplete.
185 unsigned Cnt = 0;
186 for (const VPRecipeBase &R : *VPBB)
187 RecipeNumbering[&R] = Cnt++;
188
189 for (const VPRecipeBase &R : *VPBB) {
190 if (isa<VPIRInstruction>(&R) && !isa<VPIRBasicBlock>(VPBB)) {
191 errs() << "VPIRInstructions ";
192#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
193 R.dump();
194 errs() << " ";
195#endif
196 errs() << "not in a VPIRBasicBlock!\n";
197 return false;
198 }
199 for (const VPValue *V : R.definedValues()) {
200 // Verify that we can infer a scalar type for each defined value. With
201 // assertions enabled, inferScalarType will perform some consistency
202 // checks during type inference.
203 if (!TypeInfo.inferScalarType(V)) {
204 errs() << "Failed to infer scalar type!\n";
205 return false;
206 }
207
208 for (const VPUser *U : V->users()) {
209 auto *UI = dyn_cast<VPRecipeBase>(U);
210 // TODO: check dominance of incoming values for phis properly.
211 if (!UI ||
212 isa<VPHeaderPHIRecipe, VPWidenPHIRecipe, VPPredInstPHIRecipe>(UI))
213 continue;
214
215 // If the user is in the same block, check it comes after R in the
216 // block.
217 if (UI->getParent() == VPBB) {
218 if (RecipeNumbering[UI] < RecipeNumbering[&R]) {
219 errs() << "Use before def!\n";
220 return false;
221 }
222 continue;
223 }
224
225 if (!VPDT.dominates(VPBB, UI->getParent())) {
226 errs() << "Use before def!\n";
227 return false;
228 }
229 }
230 }
231 if (const auto *EVL = dyn_cast<VPInstruction>(&R)) {
233 !verifyEVLRecipe(*EVL)) {
234 errs() << "EVL VPValue is not used correctly\n";
235 return false;
236 }
237 }
238 }
239
240 auto *IRBB = dyn_cast<VPIRBasicBlock>(VPBB);
241 if (!IRBB)
242 return true;
243
244 if (!WrappedIRBBs.insert(IRBB->getIRBasicBlock()).second) {
245 errs() << "Same IR basic block used by multiple wrapper blocks!\n";
246 return false;
247 }
248
249 return true;
250}
251
252/// Utility function that checks whether \p VPBlockVec has duplicate
253/// VPBlockBases.
254static bool hasDuplicates(const SmallVectorImpl<VPBlockBase *> &VPBlockVec) {
256 for (const auto *Block : VPBlockVec) {
257 if (!VPBlockSet.insert(Block).second)
258 return true;
259 }
260 return false;
261}
262
263bool VPlanVerifier::verifyBlock(const VPBlockBase *VPB) {
264 auto *VPBB = dyn_cast<VPBasicBlock>(VPB);
265 // Check block's condition bit.
266 if (VPB->getNumSuccessors() > 1 ||
267 (VPBB && VPBB->getParent() && VPBB->isExiting() &&
268 !VPBB->getParent()->isReplicator())) {
269 if (!VPBB || !VPBB->getTerminator()) {
270 errs() << "Block has multiple successors but doesn't "
271 "have a proper branch recipe!\n";
272 return false;
273 }
274 } else {
275 if (VPBB && VPBB->getTerminator()) {
276 errs() << "Unexpected branch recipe!\n";
277 return false;
278 }
279 }
280
281 // Check block's successors.
282 const auto &Successors = VPB->getSuccessors();
283 // There must be only one instance of a successor in block's successor list.
284 // TODO: This won't work for switch statements.
285 if (hasDuplicates(Successors)) {
286 errs() << "Multiple instances of the same successor.\n";
287 return false;
288 }
289
290 for (const VPBlockBase *Succ : Successors) {
291 // There must be a bi-directional link between block and successor.
292 const auto &SuccPreds = Succ->getPredecessors();
293 if (!is_contained(SuccPreds, VPB)) {
294 errs() << "Missing predecessor link.\n";
295 return false;
296 }
297 }
298
299 // Check block's predecessors.
300 const auto &Predecessors = VPB->getPredecessors();
301 // There must be only one instance of a predecessor in block's predecessor
302 // list.
303 // TODO: This won't work for switch statements.
304 if (hasDuplicates(Predecessors)) {
305 errs() << "Multiple instances of the same predecessor.\n";
306 return false;
307 }
308
309 for (const VPBlockBase *Pred : Predecessors) {
310 // Block and predecessor must be inside the same region.
311 if (Pred->getParent() != VPB->getParent()) {
312 errs() << "Predecessor is not in the same region.\n";
313 return false;
314 }
315
316 // There must be a bi-directional link between block and predecessor.
317 const auto &PredSuccs = Pred->getSuccessors();
318 if (!is_contained(PredSuccs, VPB)) {
319 errs() << "Missing successor link.\n";
320 return false;
321 }
322 }
323 return !VPBB || verifyVPBasicBlock(VPBB);
324}
325
326bool VPlanVerifier::verifyBlocksInRegion(const VPRegionBlock *Region) {
327 for (const VPBlockBase *VPB : vp_depth_first_shallow(Region->getEntry())) {
328 // Check block's parent.
329 if (VPB->getParent() != Region) {
330 errs() << "VPBlockBase has wrong parent\n";
331 return false;
332 }
333
334 if (!verifyBlock(VPB))
335 return false;
336 }
337 return true;
338}
339
340bool VPlanVerifier::verifyRegion(const VPRegionBlock *Region) {
341 const VPBlockBase *Entry = Region->getEntry();
342 const VPBlockBase *Exiting = Region->getExiting();
343
344 // Entry and Exiting shouldn't have any predecessor/successor, respectively.
345 if (Entry->getNumPredecessors() != 0) {
346 errs() << "region entry block has predecessors\n";
347 return false;
348 }
349 if (Exiting->getNumSuccessors() != 0) {
350 errs() << "region exiting block has successors\n";
351 return false;
352 }
353
354 return verifyBlocksInRegion(Region);
355}
356
357bool VPlanVerifier::verifyRegionRec(const VPRegionBlock *Region) {
358 // Recurse inside nested regions and check all blocks inside the region.
359 return verifyRegion(Region) &&
361 [this](const VPBlockBase *VPB) {
362 const auto *SubRegion = dyn_cast<VPRegionBlock>(VPB);
363 return !SubRegion || verifyRegionRec(SubRegion);
364 });
365}
366
367bool VPlanVerifier::verify(const VPlan &Plan) {
369 [this](const VPBlockBase *VPB) { return !verifyBlock(VPB); }))
370 return false;
371
372 const VPRegionBlock *TopRegion = Plan.getVectorLoopRegion();
373 if (!verifyRegionRec(TopRegion))
374 return false;
375
376 if (TopRegion->getParent()) {
377 errs() << "VPlan Top Region should have no parent.\n";
378 return false;
379 }
380
381 const VPBasicBlock *Entry = dyn_cast<VPBasicBlock>(TopRegion->getEntry());
382 if (!Entry) {
383 errs() << "VPlan entry block is not a VPBasicBlock\n";
384 return false;
385 }
386
387 if (!isa<VPCanonicalIVPHIRecipe>(&*Entry->begin())) {
388 errs() << "VPlan vector loop header does not start with a "
389 "VPCanonicalIVPHIRecipe\n";
390 return false;
391 }
392
393 const VPBasicBlock *Exiting = dyn_cast<VPBasicBlock>(TopRegion->getExiting());
394 if (!Exiting) {
395 errs() << "VPlan exiting block is not a VPBasicBlock\n";
396 return false;
397 }
398
399 if (Exiting->empty()) {
400 errs() << "VPlan vector loop exiting block must end with BranchOnCount or "
401 "BranchOnCond VPInstruction but is empty\n";
402 return false;
403 }
404
405 auto *LastInst = dyn_cast<VPInstruction>(std::prev(Exiting->end()));
406 if (!LastInst || (LastInst->getOpcode() != VPInstruction::BranchOnCount &&
407 LastInst->getOpcode() != VPInstruction::BranchOnCond)) {
408 errs() << "VPlan vector loop exit must end with BranchOnCount or "
409 "BranchOnCond VPInstruction\n";
410 return false;
411 }
412
413 return true;
414}
415
417 VPDominatorTree VPDT;
418 VPDT.recalculate(const_cast<VPlan &>(Plan));
419 VPTypeAnalysis TypeInfo(
420 const_cast<VPlan &>(Plan).getCanonicalIV()->getScalarType());
421 VPlanVerifier Verifier(VPDT, TypeInfo);
422 return Verifier.verify(Plan);
423}
@ 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:3528
iterator end()
Definition: VPlan.h:3565
iterator begin()
Recipe iterator methods.
Definition: VPlan.h:3563
bool empty() const
Definition: VPlan.h:3574
VPBlockBase is the building block of the Hierarchical Control-Flow Graph.
Definition: VPlan.h:397
VPRegionBlock * getParent()
Definition: VPlan.h:489
size_t getNumSuccessors() const
Definition: VPlan.h:535
const VPBlocksTy & getPredecessors() const
Definition: VPlan.h:520
const VPBasicBlock * getEntryBasicBlock() const
Definition: VPlan.cpp:158
const VPBlocksTy & getSuccessors() const
Definition: VPlan.h:514
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:1192
unsigned getOpcode() const
Definition: VPlan.h:1310
VPRecipeBase is a base class modeling a sequence of one or more output IR instructions.
Definition: VPlan.h:714
A recipe to represent inloop reduction operations with vector-predication intrinsics,...
Definition: VPlan.h:2728
VPRegionBlock represents a collection of VPBasicBlocks and VPRegionBlocks which form a Single-Entry-S...
Definition: VPlan.h:3705
const VPBlockBase * getEntry() const
Definition: VPlan.h:3741
bool isReplicator() const
An indicator whether this region is to generate multiple replicated instances of output IR correspond...
Definition: VPlan.h:3773
const VPBlockBase * getExiting() const
Definition: VPlan.h:3753
VPScalarCastRecipe is a recipe to create scalar cast instructions.
Definition: VPlan.h:1579
An analysis for type-inference for VPValues.
Definition: VPlanAnalysis.h:40
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:1480
VPlan models a candidate for vectorization, encoding various decisions take to produce efficient outp...
Definition: VPlan.h:3804
VPBasicBlock * getEntry()
Definition: VPlan.h:3917
VPRegionBlock * getVectorLoopRegion()
Returns the VPRegionBlock of the vector loop.
Definition: VPlan.cpp:1052
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:3141