1 //===-- SafepointIRVerifier.cpp - Verify gc.statepoint invariants ---------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // Run a sanity check on the IR to ensure that Safepoints - if they've been 11 // inserted - were inserted correctly. In particular, look for use of 12 // non-relocated values after a safepoint. It's primary use is to check the 13 // correctness of safepoint insertion immediately after insertion, but it can 14 // also be used to verify that later transforms have not found a way to break 15 // safepoint semenatics. 16 // 17 // In its current form, this verify checks a property which is sufficient, but 18 // not neccessary for correctness. There are some cases where an unrelocated 19 // pointer can be used after the safepoint. Consider this example: 20 // 21 // a = ... 22 // b = ... 23 // (a',b') = safepoint(a,b) 24 // c = cmp eq a b 25 // br c, ..., .... 26 // 27 // Because it is valid to reorder 'c' above the safepoint, this is legal. In 28 // practice, this is a somewhat uncommon transform, but CodeGenPrep does create 29 // idioms like this. The verifier knows about these cases and avoids reporting 30 // false positives. 31 // 32 //===----------------------------------------------------------------------===// 33 34 #include "llvm/ADT/DenseSet.h" 35 #include "llvm/ADT/PostOrderIterator.h" 36 #include "llvm/ADT/SetOperations.h" 37 #include "llvm/ADT/SetVector.h" 38 #include "llvm/IR/BasicBlock.h" 39 #include "llvm/IR/Dominators.h" 40 #include "llvm/IR/Function.h" 41 #include "llvm/IR/Instructions.h" 42 #include "llvm/IR/Intrinsics.h" 43 #include "llvm/IR/IntrinsicInst.h" 44 #include "llvm/IR/Module.h" 45 #include "llvm/IR/Value.h" 46 #include "llvm/IR/SafepointIRVerifier.h" 47 #include "llvm/IR/Statepoint.h" 48 #include "llvm/Support/Debug.h" 49 #include "llvm/Support/CommandLine.h" 50 #include "llvm/Support/raw_ostream.h" 51 52 #define DEBUG_TYPE "safepoint-ir-verifier" 53 54 using namespace llvm; 55 56 /// This option is used for writing test cases. Instead of crashing the program 57 /// when verification fails, report a message to the console (for FileCheck 58 /// usage) and continue execution as if nothing happened. 59 static cl::opt<bool> PrintOnly("safepoint-ir-verifier-print-only", 60 cl::init(false)); 61 62 static void Verify(const Function &F, const DominatorTree &DT); 63 64 namespace { 65 struct SafepointIRVerifier : public FunctionPass { 66 static char ID; // Pass identification, replacement for typeid 67 DominatorTree DT; 68 SafepointIRVerifier() : FunctionPass(ID) { 69 initializeSafepointIRVerifierPass(*PassRegistry::getPassRegistry()); 70 } 71 72 bool runOnFunction(Function &F) override { 73 DT.recalculate(F); 74 Verify(F, DT); 75 return false; // no modifications 76 } 77 78 void getAnalysisUsage(AnalysisUsage &AU) const override { 79 AU.setPreservesAll(); 80 } 81 82 StringRef getPassName() const override { return "safepoint verifier"; } 83 }; 84 } // namespace 85 86 void llvm::verifySafepointIR(Function &F) { 87 SafepointIRVerifier pass; 88 pass.runOnFunction(F); 89 } 90 91 char SafepointIRVerifier::ID = 0; 92 93 FunctionPass *llvm::createSafepointIRVerifierPass() { 94 return new SafepointIRVerifier(); 95 } 96 97 INITIALIZE_PASS_BEGIN(SafepointIRVerifier, "verify-safepoint-ir", 98 "Safepoint IR Verifier", false, true) 99 INITIALIZE_PASS_END(SafepointIRVerifier, "verify-safepoint-ir", 100 "Safepoint IR Verifier", false, true) 101 102 static bool isGCPointerType(Type *T) { 103 if (auto *PT = dyn_cast<PointerType>(T)) 104 // For the sake of this example GC, we arbitrarily pick addrspace(1) as our 105 // GC managed heap. We know that a pointer into this heap needs to be 106 // updated and that no other pointer does. 107 return (1 == PT->getAddressSpace()); 108 return false; 109 } 110 111 static bool containsGCPtrType(Type *Ty) { 112 if (isGCPointerType(Ty)) 113 return true; 114 if (VectorType *VT = dyn_cast<VectorType>(Ty)) 115 return isGCPointerType(VT->getScalarType()); 116 if (ArrayType *AT = dyn_cast<ArrayType>(Ty)) 117 return containsGCPtrType(AT->getElementType()); 118 if (StructType *ST = dyn_cast<StructType>(Ty)) 119 return std::any_of(ST->subtypes().begin(), ST->subtypes().end(), 120 containsGCPtrType); 121 return false; 122 } 123 124 // Debugging aid -- prints a [Begin, End) range of values. 125 template<typename IteratorTy> 126 static void PrintValueSet(raw_ostream &OS, IteratorTy Begin, IteratorTy End) { 127 OS << "[ "; 128 while (Begin != End) { 129 OS << **Begin << " "; 130 ++Begin; 131 } 132 OS << "]"; 133 } 134 135 /// The verifier algorithm is phrased in terms of availability. The set of 136 /// values "available" at a given point in the control flow graph is the set of 137 /// correctly relocated value at that point, and is a subset of the set of 138 /// definitions dominating that point. 139 140 using AvailableValueSet = DenseSet<const Value *>; 141 142 /// State we compute and track per basic block. 143 struct BasicBlockState { 144 // Set of values available coming in, before the phi nodes 145 AvailableValueSet AvailableIn; 146 147 // Set of values available going out 148 AvailableValueSet AvailableOut; 149 150 // AvailableOut minus AvailableIn. 151 // All elements are Instructions 152 AvailableValueSet Contribution; 153 154 // True if this block contains a safepoint and thus AvailableIn does not 155 // contribute to AvailableOut. 156 bool Cleared = false; 157 }; 158 159 /// A given derived pointer can have multiple base pointers through phi/selects. 160 /// This type indicates when the base pointer is exclusively constant 161 /// (ExclusivelySomeConstant), and if that constant is proven to be exclusively 162 /// null, we record that as ExclusivelyNull. In all other cases, the BaseType is 163 /// NonConstant. 164 enum BaseType { 165 NonConstant = 1, // Base pointers is not exclusively constant. 166 ExclusivelyNull, 167 ExclusivelySomeConstant // Base pointers for a given derived pointer is from a 168 // set of constants, but they are not exclusively 169 // null. 170 }; 171 172 /// Return the baseType for Val which states whether Val is exclusively 173 /// derived from constant/null, or not exclusively derived from constant. 174 /// Val is exclusively derived off a constant base when all operands of phi and 175 /// selects are derived off a constant base. 176 static enum BaseType getBaseType(const Value *Val) { 177 178 SmallVector<const Value *, 32> Worklist; 179 DenseSet<const Value *> Visited; 180 bool isExclusivelyDerivedFromNull = true; 181 Worklist.push_back(Val); 182 // Strip through all the bitcasts and geps to get base pointer. Also check for 183 // the exclusive value when there can be multiple base pointers (through phis 184 // or selects). 185 while(!Worklist.empty()) { 186 const Value *V = Worklist.pop_back_val(); 187 if (!Visited.insert(V).second) 188 continue; 189 190 if (const auto *CI = dyn_cast<CastInst>(V)) { 191 Worklist.push_back(CI->stripPointerCasts()); 192 continue; 193 } 194 if (const auto *GEP = dyn_cast<GetElementPtrInst>(V)) { 195 Worklist.push_back(GEP->getPointerOperand()); 196 continue; 197 } 198 // Push all the incoming values of phi node into the worklist for 199 // processing. 200 if (const auto *PN = dyn_cast<PHINode>(V)) { 201 for (Value *InV: PN->incoming_values()) 202 Worklist.push_back(InV); 203 continue; 204 } 205 if (const auto *SI = dyn_cast<SelectInst>(V)) { 206 // Push in the true and false values 207 Worklist.push_back(SI->getTrueValue()); 208 Worklist.push_back(SI->getFalseValue()); 209 continue; 210 } 211 if (isa<Constant>(V)) { 212 // We found at least one base pointer which is non-null, so this derived 213 // pointer is not exclusively derived from null. 214 if (V != Constant::getNullValue(V->getType())) 215 isExclusivelyDerivedFromNull = false; 216 // Continue processing the remaining values to make sure it's exclusively 217 // constant. 218 continue; 219 } 220 // At this point, we know that the base pointer is not exclusively 221 // constant. 222 return BaseType::NonConstant; 223 } 224 // Now, we know that the base pointer is exclusively constant, but we need to 225 // differentiate between exclusive null constant and non-null constant. 226 return isExclusivelyDerivedFromNull ? BaseType::ExclusivelyNull 227 : BaseType::ExclusivelySomeConstant; 228 } 229 230 static bool isNotExclusivelyConstantDerived(const Value *V) { 231 return getBaseType(V) == BaseType::NonConstant; 232 } 233 234 namespace { 235 class InstructionVerifier; 236 237 /// Builds BasicBlockState for each BB of the function. 238 /// It can traverse function for verification and provides all required 239 /// information. 240 class GCPtrTracker { 241 const Function &F; 242 SpecificBumpPtrAllocator<BasicBlockState> BSAllocator; 243 DenseMap<const BasicBlock *, BasicBlockState *> BlockMap; 244 // This set contains defs of unrelocated pointers that are proved to be legal 245 // and don't need verification. 246 DenseSet<const Instruction *> ValidUnrelocatedDefs; 247 248 public: 249 GCPtrTracker(const Function &F, const DominatorTree &DT); 250 251 BasicBlockState *getBasicBlockState(const BasicBlock *BB); 252 const BasicBlockState *getBasicBlockState(const BasicBlock *BB) const; 253 254 /// Traverse each BB of the function and call 255 /// InstructionVerifier::verifyInstruction for each possibly invalid 256 /// instruction. 257 /// It destructively modifies GCPtrTracker so it's passed via rvalue reference 258 /// in order to prohibit further usages of GCPtrTracker as it'll be in 259 /// inconsistent state. 260 static void verifyFunction(GCPtrTracker &&Tracker, 261 InstructionVerifier &Verifier); 262 263 private: 264 /// Returns true if the instruction may be safely skipped during verification. 265 bool instructionMayBeSkipped(const Instruction *I) const; 266 267 /// Iterates over all BBs from BlockMap and recalculates AvailableIn/Out for 268 /// each of them until it converges. 269 void recalculateBBsStates(); 270 271 /// Remove from Contribution all defs that legally produce unrelocated 272 /// pointers and saves them to ValidUnrelocatedDefs. 273 /// Though Contribution should belong to BBS it is passed separately with 274 /// different const-modifier in order to emphasize (and guarantee) that only 275 /// Contribution will be changed. 276 /// Returns true if Contribution was changed otherwise false. 277 bool removeValidUnrelocatedDefs(const BasicBlock *BB, 278 const BasicBlockState *BBS, 279 AvailableValueSet &Contribution); 280 281 /// Gather all the definitions dominating the start of BB into Result. This is 282 /// simply the defs introduced by every dominating basic block and the 283 /// function arguments. 284 void gatherDominatingDefs(const BasicBlock *BB, AvailableValueSet &Result, 285 const DominatorTree &DT); 286 287 /// Compute the AvailableOut set for BB, based on the BasicBlockState BBS, 288 /// which is the BasicBlockState for BB. 289 /// ContributionChanged is set when the verifier runs for the first time 290 /// (in this case Contribution was changed from 'empty' to its initial state) 291 /// or when Contribution of this BB was changed since last computation. 292 static void transferBlock(const BasicBlock *BB, BasicBlockState &BBS, 293 bool ContributionChanged); 294 295 /// Model the effect of an instruction on the set of available values. 296 static void transferInstruction(const Instruction &I, bool &Cleared, 297 AvailableValueSet &Available); 298 }; 299 300 /// It is a visitor for GCPtrTracker::verifyFunction. It decides if the 301 /// instruction (which uses heap reference) is legal or not, given our safepoint 302 /// semantics. 303 class InstructionVerifier { 304 bool AnyInvalidUses = false; 305 306 public: 307 void verifyInstruction(const GCPtrTracker *Tracker, const Instruction &I, 308 const AvailableValueSet &AvailableSet); 309 310 bool hasAnyInvalidUses() const { return AnyInvalidUses; } 311 312 private: 313 void reportInvalidUse(const Value &V, const Instruction &I); 314 }; 315 } // end anonymous namespace 316 317 GCPtrTracker::GCPtrTracker(const Function &F, const DominatorTree &DT) : F(F) { 318 // First, calculate Contribution of each BB. 319 for (const BasicBlock &BB : F) { 320 BasicBlockState *BBS = new (BSAllocator.Allocate()) BasicBlockState; 321 for (const auto &I : BB) 322 transferInstruction(I, BBS->Cleared, BBS->Contribution); 323 BlockMap[&BB] = BBS; 324 } 325 326 // Initialize AvailableIn/Out sets of each BB using only information about 327 // dominating BBs. 328 for (auto &BBI : BlockMap) { 329 gatherDominatingDefs(BBI.first, BBI.second->AvailableIn, DT); 330 transferBlock(BBI.first, *BBI.second, true); 331 } 332 333 // Simulate the flow of defs through the CFG and recalculate AvailableIn/Out 334 // sets of each BB until it converges. If any def is proved to be an 335 // unrelocated pointer, it will be removed from all BBSs. 336 recalculateBBsStates(); 337 } 338 339 BasicBlockState *GCPtrTracker::getBasicBlockState(const BasicBlock *BB) { 340 auto it = BlockMap.find(BB); 341 assert(it != BlockMap.end() && 342 "No such BB in BlockMap! Probably BB from another function"); 343 return it->second; 344 } 345 346 const BasicBlockState *GCPtrTracker::getBasicBlockState( 347 const BasicBlock *BB) const { 348 return const_cast<GCPtrTracker *>(this)->getBasicBlockState(BB); 349 } 350 351 bool GCPtrTracker::instructionMayBeSkipped(const Instruction *I) const { 352 return ValidUnrelocatedDefs.count(I); 353 } 354 355 void GCPtrTracker::verifyFunction(GCPtrTracker &&Tracker, 356 InstructionVerifier &Verifier) { 357 // We need RPO here to a) report always the first error b) report errors in 358 // same order from run to run. 359 ReversePostOrderTraversal<const Function *> RPOT(&Tracker.F); 360 for (const BasicBlock *BB : RPOT) { 361 BasicBlockState *BBS = Tracker.getBasicBlockState(BB); 362 // We destructively modify AvailableIn as we traverse the block instruction 363 // by instruction. 364 AvailableValueSet &AvailableSet = BBS->AvailableIn; 365 for (const Instruction &I : *BB) { 366 if (Tracker.instructionMayBeSkipped(&I)) 367 continue; // This instruction shouldn't be added to AvailableSet. 368 369 Verifier.verifyInstruction(&Tracker, I, AvailableSet); 370 371 // Model the effect of current instruction on AvailableSet to keep the set 372 // relevant at each point of BB. 373 bool Cleared = false; 374 transferInstruction(I, Cleared, AvailableSet); 375 (void)Cleared; 376 } 377 } 378 } 379 380 void GCPtrTracker::recalculateBBsStates() { 381 SetVector<const BasicBlock *> Worklist; 382 // TODO: This order is suboptimal, it's better to replace it with priority 383 // queue where priority is RPO number of BB. 384 for (auto &BBI : BlockMap) 385 Worklist.insert(BBI.first); 386 387 // This loop iterates the AvailableIn/Out sets until it converges. 388 // The AvailableIn and AvailableOut sets decrease as we iterate. 389 while (!Worklist.empty()) { 390 const BasicBlock *BB = Worklist.pop_back_val(); 391 BasicBlockState *BBS = BlockMap[BB]; 392 393 size_t OldInCount = BBS->AvailableIn.size(); 394 for (const BasicBlock *PBB : predecessors(BB)) 395 set_intersect(BBS->AvailableIn, BlockMap[PBB]->AvailableOut); 396 397 assert(OldInCount >= BBS->AvailableIn.size() && "invariant!"); 398 399 bool InputsChanged = OldInCount != BBS->AvailableIn.size(); 400 bool ContributionChanged = 401 removeValidUnrelocatedDefs(BB, BBS, BBS->Contribution); 402 if (!InputsChanged && !ContributionChanged) 403 continue; 404 405 size_t OldOutCount = BBS->AvailableOut.size(); 406 transferBlock(BB, *BBS, ContributionChanged); 407 if (OldOutCount != BBS->AvailableOut.size()) { 408 assert(OldOutCount > BBS->AvailableOut.size() && "invariant!"); 409 Worklist.insert(succ_begin(BB), succ_end(BB)); 410 } 411 } 412 } 413 414 bool GCPtrTracker::removeValidUnrelocatedDefs(const BasicBlock *BB, 415 const BasicBlockState *BBS, 416 AvailableValueSet &Contribution) { 417 assert(&BBS->Contribution == &Contribution && 418 "Passed Contribution should be from the passed BasicBlockState!"); 419 AvailableValueSet AvailableSet = BBS->AvailableIn; 420 bool ContributionChanged = false; 421 for (const Instruction &I : *BB) { 422 bool ProducesUnrelocatedPointer = false; 423 if ((isa<GetElementPtrInst>(I) || isa<BitCastInst>(I)) && 424 containsGCPtrType(I.getType())) { 425 // GEP/bitcast of unrelocated pointer is legal by itself but this 426 // def shouldn't appear in any AvailableSet. 427 for (const Value *V : I.operands()) 428 if (containsGCPtrType(V->getType()) && 429 isNotExclusivelyConstantDerived(V) && !AvailableSet.count(V)) { 430 ProducesUnrelocatedPointer = true; 431 break; 432 } 433 } 434 if (!ProducesUnrelocatedPointer) { 435 bool Cleared = false; 436 transferInstruction(I, Cleared, AvailableSet); 437 (void)Cleared; 438 } else { 439 // Remove def of unrelocated pointer from Contribution of this BB 440 // and trigger update of all its successors. 441 Contribution.erase(&I); 442 ValidUnrelocatedDefs.insert(&I); 443 DEBUG(dbgs() << "Removing " << I << " from Contribution of " 444 << BB->getName() << "\n"); 445 ContributionChanged = true; 446 } 447 } 448 return ContributionChanged; 449 } 450 451 void GCPtrTracker::gatherDominatingDefs(const BasicBlock *BB, 452 AvailableValueSet &Result, 453 const DominatorTree &DT) { 454 DomTreeNode *DTN = DT[const_cast<BasicBlock *>(BB)]; 455 456 while (DTN->getIDom()) { 457 DTN = DTN->getIDom(); 458 const auto &Defs = BlockMap[DTN->getBlock()]->Contribution; 459 Result.insert(Defs.begin(), Defs.end()); 460 // If this block is 'Cleared', then nothing LiveIn to this block can be 461 // available after this block completes. Note: This turns out to be 462 // really important for reducing memory consuption of the initial available 463 // sets and thus peak memory usage by this verifier. 464 if (BlockMap[DTN->getBlock()]->Cleared) 465 return; 466 } 467 468 for (const Argument &A : BB->getParent()->args()) 469 if (containsGCPtrType(A.getType())) 470 Result.insert(&A); 471 } 472 473 void GCPtrTracker::transferBlock(const BasicBlock *BB, BasicBlockState &BBS, 474 bool ContributionChanged) { 475 const AvailableValueSet &AvailableIn = BBS.AvailableIn; 476 AvailableValueSet &AvailableOut = BBS.AvailableOut; 477 478 if (BBS.Cleared) { 479 // AvailableOut will change only when Contribution changed. 480 if (ContributionChanged) 481 AvailableOut = BBS.Contribution; 482 } else { 483 // Otherwise, we need to reduce the AvailableOut set by things which are no 484 // longer in our AvailableIn 485 AvailableValueSet Temp = BBS.Contribution; 486 set_union(Temp, AvailableIn); 487 AvailableOut = std::move(Temp); 488 } 489 490 DEBUG(dbgs() << "Transfered block " << BB->getName() << " from "; 491 PrintValueSet(dbgs(), AvailableIn.begin(), AvailableIn.end()); 492 dbgs() << " to "; 493 PrintValueSet(dbgs(), AvailableOut.begin(), AvailableOut.end()); 494 dbgs() << "\n";); 495 } 496 497 void GCPtrTracker::transferInstruction(const Instruction &I, bool &Cleared, 498 AvailableValueSet &Available) { 499 if (isStatepoint(I)) { 500 Cleared = true; 501 Available.clear(); 502 } else if (containsGCPtrType(I.getType())) 503 Available.insert(&I); 504 } 505 506 void InstructionVerifier::verifyInstruction( 507 const GCPtrTracker *Tracker, const Instruction &I, 508 const AvailableValueSet &AvailableSet) { 509 if (const PHINode *PN = dyn_cast<PHINode>(&I)) { 510 if (containsGCPtrType(PN->getType())) 511 for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i) { 512 const BasicBlock *InBB = PN->getIncomingBlock(i); 513 const Value *InValue = PN->getIncomingValue(i); 514 515 if (isNotExclusivelyConstantDerived(InValue) && 516 !Tracker->getBasicBlockState(InBB)->AvailableOut.count(InValue)) 517 reportInvalidUse(*InValue, *PN); 518 } 519 } else if (isa<CmpInst>(I) && 520 containsGCPtrType(I.getOperand(0)->getType())) { 521 Value *LHS = I.getOperand(0), *RHS = I.getOperand(1); 522 enum BaseType baseTyLHS = getBaseType(LHS), 523 baseTyRHS = getBaseType(RHS); 524 525 // Returns true if LHS and RHS are unrelocated pointers and they are 526 // valid unrelocated uses. 527 auto hasValidUnrelocatedUse = [&AvailableSet, baseTyLHS, baseTyRHS, &LHS, 528 &RHS] () { 529 // A cmp instruction has valid unrelocated pointer operands only if 530 // both operands are unrelocated pointers. 531 // In the comparison between two pointers, if one is an unrelocated 532 // use, the other *should be* an unrelocated use, for this 533 // instruction to contain valid unrelocated uses. This unrelocated 534 // use can be a null constant as well, or another unrelocated 535 // pointer. 536 if (AvailableSet.count(LHS) || AvailableSet.count(RHS)) 537 return false; 538 // Constant pointers (that are not exclusively null) may have 539 // meaning in different VMs, so we cannot reorder the compare 540 // against constant pointers before the safepoint. In other words, 541 // comparison of an unrelocated use against a non-null constant 542 // maybe invalid. 543 if ((baseTyLHS == BaseType::ExclusivelySomeConstant && 544 baseTyRHS == BaseType::NonConstant) || 545 (baseTyLHS == BaseType::NonConstant && 546 baseTyRHS == BaseType::ExclusivelySomeConstant)) 547 return false; 548 // All other cases are valid cases enumerated below: 549 // 1. Comparison between an exlusively derived null pointer and a 550 // constant base pointer. 551 // 2. Comparison between an exlusively derived null pointer and a 552 // non-constant unrelocated base pointer. 553 // 3. Comparison between 2 unrelocated pointers. 554 return true; 555 }; 556 if (!hasValidUnrelocatedUse()) { 557 // Print out all non-constant derived pointers that are unrelocated 558 // uses, which are invalid. 559 if (baseTyLHS == BaseType::NonConstant && !AvailableSet.count(LHS)) 560 reportInvalidUse(*LHS, I); 561 if (baseTyRHS == BaseType::NonConstant && !AvailableSet.count(RHS)) 562 reportInvalidUse(*RHS, I); 563 } 564 } else { 565 for (const Value *V : I.operands()) 566 if (containsGCPtrType(V->getType()) && 567 isNotExclusivelyConstantDerived(V) && !AvailableSet.count(V)) 568 reportInvalidUse(*V, I); 569 } 570 } 571 572 void InstructionVerifier::reportInvalidUse(const Value &V, 573 const Instruction &I) { 574 errs() << "Illegal use of unrelocated value found!\n"; 575 errs() << "Def: " << V << "\n"; 576 errs() << "Use: " << I << "\n"; 577 if (!PrintOnly) 578 abort(); 579 AnyInvalidUses = true; 580 } 581 582 static void Verify(const Function &F, const DominatorTree &DT) { 583 DEBUG(dbgs() << "Verifying gc pointers in function: " << F.getName() << "\n"); 584 if (PrintOnly) 585 dbgs() << "Verifying gc pointers in function: " << F.getName() << "\n"; 586 587 GCPtrTracker Tracker(F, DT); 588 589 // We now have all the information we need to decide if the use of a heap 590 // reference is legal or not, given our safepoint semantics. 591 592 InstructionVerifier Verifier; 593 GCPtrTracker::verifyFunction(std::move(Tracker), Verifier); 594 595 if (PrintOnly && !Verifier.hasAnyInvalidUses()) { 596 dbgs() << "No illegal uses found by SafepointIRVerifier in: " << F.getName() 597 << "\n"; 598 } 599 } 600