@@ -56,6 +56,10 @@ static llvm::cl::opt<bool> AbortOnFailure(
56
56
" verify-abort-on-failure" ,
57
57
llvm::cl::init (true ));
58
58
59
+ static llvm::cl::opt<bool > VerifyDIHoles (
60
+ " verify-di-holes" ,
61
+ llvm::cl::init (false ));
62
+
59
63
// The verifier is basically all assertions, so don't compile it with NDEBUG to
60
64
// prevent release builds from triggering spurious unused variable warnings.
61
65
@@ -4338,6 +4342,78 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
4338
4342
}
4339
4343
}
4340
4344
4345
+ // / This pass verifies that there are no hole in debug scopes at -Onone.
4346
+ void verifyDebugScopeHoles (SILBasicBlock *BB) {
4347
+ if (!VerifyDIHoles)
4348
+ return ;
4349
+
4350
+ // This check only makes sense at -Onone. Optimizations,
4351
+ // e.g. inlining, can move scopes around.
4352
+ llvm::DenseSet<const SILDebugScope *> AlreadySeenScopes;
4353
+ if (BB->getParent ()->getEffectiveOptimizationMode () !=
4354
+ OptimizationMode::NoOptimization)
4355
+ return ;
4356
+
4357
+ // Exit early if this BB is empty.
4358
+ if (BB->empty ())
4359
+ return ;
4360
+
4361
+ const SILDebugScope *LastSeenScope = nullptr ;
4362
+ for (SILInstruction &SI : *BB) {
4363
+ if (isa<AllocStackInst>(SI))
4364
+ continue ;
4365
+ LastSeenScope = SI.getDebugScope ();
4366
+ AlreadySeenScopes.insert (LastSeenScope);
4367
+ break ;
4368
+ }
4369
+ for (SILInstruction &SI : *BB) {
4370
+ // `alloc_stack` can create false positive, so we skip it
4371
+ // for now.
4372
+ if (isa<AllocStackInst>(SI))
4373
+ continue ;
4374
+
4375
+ // If we haven't seen this debug scope yet, update the
4376
+ // map and go on.
4377
+ auto *DS = SI.getDebugScope ();
4378
+ assert (DS && " Each instruction should have a debug scope" );
4379
+ if (!AlreadySeenScopes.count (DS)) {
4380
+ AlreadySeenScopes.insert (DS);
4381
+ LastSeenScope = DS;
4382
+ continue ;
4383
+ }
4384
+
4385
+ // Otherwise, we're allowed to re-enter a scope only if
4386
+ // the scope is an ancestor of the scope we're currently leaving.
4387
+ auto isAncestorScope = [](const SILDebugScope *Cur,
4388
+ const SILDebugScope *Previous) {
4389
+ const SILDebugScope *Tmp = Previous;
4390
+ assert (Tmp && " scope can't be null" );
4391
+ while (Tmp) {
4392
+ PointerUnion<const SILDebugScope *, SILFunction *> Parent =
4393
+ Tmp->Parent ;
4394
+ auto *ParentScope = Parent.dyn_cast <const SILDebugScope *>();
4395
+ if (!ParentScope)
4396
+ break ;
4397
+ if (ParentScope == Cur)
4398
+ return true ;
4399
+ Tmp = ParentScope;
4400
+ }
4401
+ return false ;
4402
+ };
4403
+
4404
+ if (isAncestorScope (DS, LastSeenScope)) {
4405
+ LastSeenScope = DS;
4406
+ continue ;
4407
+ }
4408
+ if (DS != LastSeenScope) {
4409
+ DEBUG (llvm::dbgs () << " Broken instruction!\n " ; SI.dump ());
4410
+ require (
4411
+ DS == LastSeenScope,
4412
+ " Basic block contains a non-contiguous lexical scope at -Onone" );
4413
+ }
4414
+ }
4415
+ }
4416
+
4341
4417
void visitSILBasicBlock (SILBasicBlock *BB) {
4342
4418
// Make sure that each of the successors/predecessors of this basic block
4343
4419
// have this basic block in its predecessor/successor list.
@@ -4360,6 +4436,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
4360
4436
}
4361
4437
4362
4438
SILInstructionVisitor::visitSILBasicBlock (BB);
4439
+ verifyDebugScopeHoles (BB);
4363
4440
}
4364
4441
4365
4442
void visitBasicBlockArguments (SILBasicBlock *BB) {
0 commit comments