LLVM 19.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"
21
22#define DEBUG_TYPE "loop-vectorize"
23
24using namespace llvm;
25
26// Verify that phi-like recipes are at the beginning of \p VPBB, with no
27// other recipes in between. Also check that only header blocks contain
28// VPHeaderPHIRecipes.
29static bool verifyPhiRecipes(const VPBasicBlock *VPBB) {
30 auto RecipeI = VPBB->begin();
31 auto End = VPBB->end();
32 unsigned NumActiveLaneMaskPhiRecipes = 0;
33 const VPRegionBlock *ParentR = VPBB->getParent();
34 bool IsHeaderVPBB = ParentR && !ParentR->isReplicator() &&
35 ParentR->getEntryBasicBlock() == VPBB;
36 while (RecipeI != End && RecipeI->isPhi()) {
37 if (isa<VPActiveLaneMaskPHIRecipe>(RecipeI))
38 NumActiveLaneMaskPhiRecipes++;
39
40 if (IsHeaderVPBB && !isa<VPHeaderPHIRecipe, VPWidenPHIRecipe>(*RecipeI)) {
41 errs() << "Found non-header PHI recipe in header VPBB";
42#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
43 errs() << ": ";
44 RecipeI->dump();
45#endif
46 return false;
47 }
48
49 if (!IsHeaderVPBB && isa<VPHeaderPHIRecipe>(*RecipeI)) {
50 errs() << "Found header PHI recipe in non-header VPBB";
51#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
52 errs() << ": ";
53 RecipeI->dump();
54#endif
55 return false;
56 }
57
58 RecipeI++;
59 }
60
61 if (NumActiveLaneMaskPhiRecipes > 1) {
62 errs() << "There should be no more than one VPActiveLaneMaskPHIRecipe";
63 return false;
64 }
65
66 while (RecipeI != End) {
67 if (RecipeI->isPhi() && !isa<VPBlendRecipe>(&*RecipeI)) {
68 errs() << "Found phi-like recipe after non-phi recipe";
69
70#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
71 errs() << ": ";
72 RecipeI->dump();
73 errs() << "after\n";
74 std::prev(RecipeI)->dump();
75#endif
76 return false;
77 }
78 RecipeI++;
79 }
80 return true;
81}
82
83static bool verifyVPBasicBlock(const VPBasicBlock *VPBB,
84 const VPDominatorTree &VPDT) {
85 if (!verifyPhiRecipes(VPBB))
86 return false;
87
88 // Verify that defs in VPBB dominate all their uses. The current
89 // implementation is still incomplete.
91 unsigned Cnt = 0;
92 for (const VPRecipeBase &R : *VPBB)
93 RecipeNumbering[&R] = Cnt++;
94
95 // Set of recipe types along with VPInstruction Opcodes of all EVL-related
96 // recipes that must appear at most once in the header block.
97 DenseSet<unsigned> EVLFound;
98 const VPRecipeBase *VPWidenMemRecipe = nullptr;
99 const VPlan *Plan = VPBB->getPlan();
100 bool IsHeader = Plan->getEntry()->getNumSuccessors() == 1 &&
101 Plan->getVectorLoopRegion()->getEntry() == VPBB;
102 auto CheckEVLRecipiesInsts = [&](const VPRecipeBase *R) {
103 if (isa<VPEVLBasedIVPHIRecipe>(R)) {
104 if (!IsHeader) {
105 errs() << "EVL PHI recipe not in entry block!\n";
106 return false;
107 }
108 if (!EVLFound.insert(VPDef::VPEVLBasedIVPHISC).second) {
109 errs() << "EVL PHI recipe inserted more than once!\n";
110 return false;
111 }
112 return true;
113 }
114 if (const auto *RInst = dyn_cast<VPInstruction>(R);
115 RInst && RInst->getOpcode() == VPInstruction::ExplicitVectorLength) {
116 if (!IsHeader) {
117 errs() << "EVL instruction not in the header block!\n";
118 return false;
119 }
120 if (!EVLFound.insert(RInst->getOpcode() + VPDef::VPLastPHISC).second) {
121 errs() << "EVL instruction inserted more than once!\n";
122 return false;
123 }
124 if (VPWidenMemRecipe) {
125 errs() << "Use of EVL instruction by widen memory recipe before "
126 "definition!\n";
127 return false;
128 }
129 return true;
130 }
131 if (isa<VPWidenMemoryRecipe>(R))
132 VPWidenMemRecipe = R;
133 return true;
134 };
135
136 for (const VPRecipeBase &R : *VPBB) {
137 if (!CheckEVLRecipiesInsts(&R))
138 return false;
139 for (const VPValue *V : R.definedValues()) {
140 for (const VPUser *U : V->users()) {
141 auto *UI = dyn_cast<VPRecipeBase>(U);
142 // TODO: check dominance of incoming values for phis properly.
143 if (!UI ||
144 isa<VPHeaderPHIRecipe, VPWidenPHIRecipe, VPPredInstPHIRecipe>(UI))
145 continue;
146
147 // If the user is in the same block, check it comes after R in the
148 // block.
149 if (UI->getParent() == VPBB) {
150 if (RecipeNumbering[UI] < RecipeNumbering[&R]) {
151 errs() << "Use before def!\n";
152 return false;
153 }
154 continue;
155 }
156
157 if (!VPDT.dominates(VPBB, UI->getParent())) {
158 errs() << "Use before def!\n";
159 return false;
160 }
161 }
162 }
163 }
164 return true;
165}
166
167/// Utility function that checks whether \p VPBlockVec has duplicate
168/// VPBlockBases.
169static bool hasDuplicates(const SmallVectorImpl<VPBlockBase *> &VPBlockVec) {
171 for (const auto *Block : VPBlockVec) {
172 if (VPBlockSet.count(Block))
173 return true;
174 VPBlockSet.insert(Block);
175 }
176 return false;
177}
178
179static bool verifyBlock(const VPBlockBase *VPB, const VPDominatorTree &VPDT) {
180 auto *VPBB = dyn_cast<VPBasicBlock>(VPB);
181 // Check block's condition bit.
182 if (VPB->getNumSuccessors() > 1 ||
183 (VPBB && VPBB->getParent() && VPBB->isExiting() &&
184 !VPBB->getParent()->isReplicator())) {
185 if (!VPBB || !VPBB->getTerminator()) {
186 errs() << "Block has multiple successors but doesn't "
187 "have a proper branch recipe!\n";
188 return false;
189 }
190 } else {
191 if (VPBB && VPBB->getTerminator()) {
192 errs() << "Unexpected branch recipe!\n";
193 return false;
194 }
195 }
196
197 // Check block's successors.
198 const auto &Successors = VPB->getSuccessors();
199 // There must be only one instance of a successor in block's successor list.
200 // TODO: This won't work for switch statements.
201 if (hasDuplicates(Successors)) {
202 errs() << "Multiple instances of the same successor.\n";
203 return false;
204 }
205
206 for (const VPBlockBase *Succ : Successors) {
207 // There must be a bi-directional link between block and successor.
208 const auto &SuccPreds = Succ->getPredecessors();
209 if (!is_contained(SuccPreds, VPB)) {
210 errs() << "Missing predecessor link.\n";
211 return false;
212 }
213 }
214
215 // Check block's predecessors.
216 const auto &Predecessors = VPB->getPredecessors();
217 // There must be only one instance of a predecessor in block's predecessor
218 // list.
219 // TODO: This won't work for switch statements.
220 if (hasDuplicates(Predecessors)) {
221 errs() << "Multiple instances of the same predecessor.\n";
222 return false;
223 }
224
225 for (const VPBlockBase *Pred : Predecessors) {
226 // Block and predecessor must be inside the same region.
227 if (Pred->getParent() != VPB->getParent()) {
228 errs() << "Predecessor is not in the same region.\n";
229 return false;
230 }
231
232 // There must be a bi-directional link between block and predecessor.
233 const auto &PredSuccs = Pred->getSuccessors();
234 if (!is_contained(PredSuccs, VPB)) {
235 errs() << "Missing successor link.\n";
236 return false;
237 }
238 }
239 return !VPBB || verifyVPBasicBlock(VPBB, VPDT);
240}
241
242/// Helper function that verifies the CFG invariants of the VPBlockBases within
243/// \p Region. Checks in this function are generic for VPBlockBases. They are
244/// not specific for VPBasicBlocks or VPRegionBlocks.
246 const VPDominatorTree &VPDT) {
247 for (const VPBlockBase *VPB : vp_depth_first_shallow(Region->getEntry())) {
248 // Check block's parent.
249 if (VPB->getParent() != Region) {
250 errs() << "VPBlockBase has wrong parent\n";
251 return false;
252 }
253
254 if (!verifyBlock(VPB, VPDT))
255 return false;
256 }
257 return true;
258}
259
260/// Verify the CFG invariants of VPRegionBlock \p Region and its nested
261/// VPBlockBases. Do not recurse inside nested VPRegionBlocks.
263 const VPDominatorTree &VPDT) {
264 const VPBlockBase *Entry = Region->getEntry();
265 const VPBlockBase *Exiting = Region->getExiting();
266
267 // Entry and Exiting shouldn't have any predecessor/successor, respectively.
268 if (Entry->getNumPredecessors() != 0) {
269 errs() << "region entry block has predecessors\n";
270 return false;
271 }
272 if (Exiting->getNumSuccessors() != 0) {
273 errs() << "region exiting block has successors\n";
274 return false;
275 }
276
277 return verifyBlocksInRegion(Region, VPDT);
278}
279
280/// Verify the CFG invariants of VPRegionBlock \p Region and its nested
281/// VPBlockBases. Recurse inside nested VPRegionBlocks.
283 const VPDominatorTree &VPDT) {
284 // Recurse inside nested regions and check all blocks inside the region.
285 return verifyRegion(Region, VPDT) &&
287 [&VPDT](const VPBlockBase *VPB) {
288 const auto *SubRegion = dyn_cast<VPRegionBlock>(VPB);
289 return !SubRegion || verifyRegionRec(SubRegion, VPDT);
290 });
291}
292
294 VPDominatorTree VPDT;
295 VPDT.recalculate(const_cast<VPlan &>(Plan));
296
297 if (any_of(
299 [&VPDT](const VPBlockBase *VPB) { return !verifyBlock(VPB, VPDT); }))
300 return false;
301
302 const VPRegionBlock *TopRegion = Plan.getVectorLoopRegion();
303 if (!verifyRegionRec(TopRegion, VPDT))
304 return false;
305
306 if (TopRegion->getParent()) {
307 errs() << "VPlan Top Region should have no parent.\n";
308 return false;
309 }
310
311 const VPBasicBlock *Entry = dyn_cast<VPBasicBlock>(TopRegion->getEntry());
312 if (!Entry) {
313 errs() << "VPlan entry block is not a VPBasicBlock\n";
314 return false;
315 }
316
317 if (!isa<VPCanonicalIVPHIRecipe>(&*Entry->begin())) {
318 errs() << "VPlan vector loop header does not start with a "
319 "VPCanonicalIVPHIRecipe\n";
320 return false;
321 }
322
323 const VPBasicBlock *Exiting = dyn_cast<VPBasicBlock>(TopRegion->getExiting());
324 if (!Exiting) {
325 errs() << "VPlan exiting block is not a VPBasicBlock\n";
326 return false;
327 }
328
329 if (Exiting->empty()) {
330 errs() << "VPlan vector loop exiting block must end with BranchOnCount or "
331 "BranchOnCond VPInstruction but is empty\n";
332 return false;
333 }
334
335 auto *LastInst = dyn_cast<VPInstruction>(std::prev(Exiting->end()));
336 if (!LastInst || (LastInst->getOpcode() != VPInstruction::BranchOnCount &&
337 LastInst->getOpcode() != VPInstruction::BranchOnCond)) {
338 errs() << "VPlan vector loop exit must end with BranchOnCount or "
339 "BranchOnCond VPInstruction\n";
340 return false;
341 }
342
343 for (const auto &KV : Plan.getLiveOuts())
344 if (KV.second->getNumOperands() != 1) {
345 errs() << "live outs must have a single operand\n";
346 return false;
347 }
348
349 return true;
350}
This file builds on the ADT/GraphTraits.h file to build generic depth first graph iterator.
bool End
Definition: ELF_riscv.cpp:480
This file implements dominator tree analysis for a single level of a VPlan's H-CFG.
static bool verifyPhiRecipes(const VPBasicBlock *VPBB)
static bool verifyRegion(const VPRegionBlock *Region, const VPDominatorTree &VPDT)
Verify the CFG invariants of VPRegionBlock Region and its nested VPBlockBases.
static bool verifyRegionRec(const VPRegionBlock *Region, const VPDominatorTree &VPDT)
Verify the CFG invariants of VPRegionBlock Region and its nested VPBlockBases.
static bool verifyVPBasicBlock(const VPBasicBlock *VPBB, const VPDominatorTree &VPDT)
static bool verifyBlock(const VPBlockBase *VPB, const VPDominatorTree &VPDT)
static bool hasDuplicates(const SmallVectorImpl< VPBlockBase * > &VPBlockVec)
Utility function that checks whether VPBlockVec has duplicate VPBlockBases.
static bool verifyBlocksInRegion(const VPRegionBlock *Region, const VPDominatorTree &VPDT)
Helper function that verifies the CFG invariants of the VPBlockBases within Region.
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:
Implements a dense probed hash-table based set.
Definition: DenseSet.h:271
Core dominator tree base class.
bool dominates(const DomTreeNodeBase< NodeT > *A, const DomTreeNodeBase< NodeT > *B) const
dominates - Returns true iff A dominates B.
void recalculate(ParentType &Func)
recalculate - compute a dominator tree for the given function
BlockT * getEntry() const
Get the entry BasicBlock of the Region.
Definition: RegionInfo.h:322
Implements a dense probed hash-table based set with some number of buckets stored inline.
Definition: DenseSet.h:290
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:586
VPBasicBlock serves as the leaf of the Hierarchical Control-Flow Graph.
Definition: VPlan.h:2739
iterator end()
Definition: VPlan.h:2770
iterator begin()
Recipe iterator methods.
Definition: VPlan.h:2768
bool empty() const
Definition: VPlan.h:2779
VPBlockBase is the building block of the Hierarchical Control-Flow Graph.
Definition: VPlan.h:426
VPRegionBlock * getParent()
Definition: VPlan.h:498
size_t getNumSuccessors() const
Definition: VPlan.h:543
const VPBlocksTy & getPredecessors() const
Definition: VPlan.h:528
VPlan * getPlan()
Definition: VPlan.cpp:148
const VPBasicBlock * getEntryBasicBlock() const
Definition: VPlan.cpp:153
const VPBlocksTy & getSuccessors() const
Definition: VPlan.h:523
VPRecipeBase is a base class modeling a sequence of one or more output IR instructions.
Definition: VPlan.h:718
VPRegionBlock represents a collection of VPBasicBlocks and VPRegionBlocks which form a Single-Entry-S...
Definition: VPlan.h:2872
const VPBlockBase * getEntry() const
Definition: VPlan.h:2911
bool isReplicator() const
An indicator whether this region is to generate multiple replicated instances of output IR correspond...
Definition: VPlan.h:2943
const VPBlockBase * getExiting() const
Definition: VPlan.h:2923
This class augments VPValue with operands which provide the inverse def-use edges from VPValue's user...
Definition: VPlanValue.h:203
VPlan models a candidate for vectorization, encoding various decisions take to produce efficient outp...
Definition: VPlan.h:2973
VPBasicBlock * getEntry()
Definition: VPlan.h:3066
VPRegionBlock * getVectorLoopRegion()
Returns the VPRegionBlock of the vector loop.
Definition: VPlan.h:3157
const MapVector< PHINode *, VPLiveOut * > & getLiveOuts() const
Definition: VPlan.h:3181
std::pair< iterator, bool > insert(const ValueT &V)
Definition: DenseSet.h:206
size_type count(const_arg_type_t< ValueT > V) const
Return 1 if the specified key is in the set, 0 otherwise.
Definition: DenseSet.h:97
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:1722
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:1729
raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition: STLExtras.h:1879
bool verifyVPlanIsValid(const VPlan &Plan)
Verify invariants for general VPlans.