Skip to content

Commit d01c9fa

Browse files
authored
Merge pull request #13491 from dcci/lexicalscopes
2 parents 94ca715 + a4dba76 commit d01c9fa

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

lib/SIL/SILVerifier.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ static llvm::cl::opt<bool> AbortOnFailure(
5656
"verify-abort-on-failure",
5757
llvm::cl::init(true));
5858

59+
static llvm::cl::opt<bool> VerifyDIHoles(
60+
"verify-di-holes",
61+
llvm::cl::init(false));
62+
5963
// The verifier is basically all assertions, so don't compile it with NDEBUG to
6064
// prevent release builds from triggering spurious unused variable warnings.
6165

@@ -4338,6 +4342,78 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
43384342
}
43394343
}
43404344

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+
43414417
void visitSILBasicBlock(SILBasicBlock *BB) {
43424418
// Make sure that each of the successors/predecessors of this basic block
43434419
// have this basic block in its predecessor/successor list.
@@ -4360,6 +4436,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
43604436
}
43614437

43624438
SILInstructionVisitor::visitSILBasicBlock(BB);
4439+
verifyDebugScopeHoles(BB);
43634440
}
43644441

43654442
void visitBasicBlockArguments(SILBasicBlock *BB) {

0 commit comments

Comments
 (0)