Skip to content

Commit d96306f

Browse files
committed
Don't run AccessEnforcementSelection on deserialized SIL and add verification.
This is a module pass, so was processing a combination of raw and deserialized canonical SIL. The analysis makes structural assumptions that only hold prior to mandatory inlining. Add more verification of the structural properties. Teach the pass to skip over canonical SIL, which only works because closures must be serialized/deserialized in the same module as their parent. Fixes <rdar://38042781> [Repl] crash while running SILOptimizer
1 parent 6ab8d7d commit d96306f

File tree

2 files changed

+59
-0
lines changed

2 files changed

+59
-0
lines changed

lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,20 @@ void SelectEnforcement::analyzeUsesOfBox(SingleValueInstruction *source) {
244244
// capture and, for some reason, the closure is dead.
245245
}
246246

247+
// Verify that accesses are not nested before mandatory inlining.
248+
// Closure captures should also not be nested within an access.
249+
static void checkUsesOfAccess(BeginAccessInst *access) {
250+
#ifndef NDEBUG
251+
// These conditions are only true prior to mandatory inlining.
252+
assert(!access->getFunction()->wasDeserializedCanonical());
253+
for (auto *use : access->getUses()) {
254+
auto user = use->getUser();
255+
assert(!isa<BeginAccessInst>(user));
256+
assert(!isa<PartialApplyInst>(user));
257+
}
258+
#endif
259+
}
260+
247261
void SelectEnforcement::analyzeProjection(ProjectBoxInst *projection) {
248262
for (auto *use : projection->getUses()) {
249263
auto user = use->getUser();
@@ -253,6 +267,8 @@ void SelectEnforcement::analyzeProjection(ProjectBoxInst *projection) {
253267
if (access->getEnforcement() == SILAccessEnforcement::Unknown)
254268
Accesses.push_back(access);
255269

270+
checkUsesOfAccess(access);
271+
256272
continue;
257273
}
258274
if (isa<PartialApplyInst>(user))
@@ -564,16 +580,26 @@ void AccessEnforcementSelection::run() {
564580
void AccessEnforcementSelection::processFunction(SILFunction *F) {
565581
DEBUG(llvm::dbgs() << "Access Enforcement Selection in " << F->getName()
566582
<< "\n");
583+
584+
// This ModuleTransform needs to analyze closures and their parent scopes in
585+
// the same pass, and the parent needs to be analyzed before the closure.
567586
#ifndef NDEBUG
568587
auto *CSA = getAnalysis<ClosureScopeAnalysis>();
569588
if (isNonEscapingClosure(F->getLoweredFunctionType())) {
570589
for (auto *scopeF : CSA->getClosureScopes(F)) {
571590
DEBUG(llvm::dbgs() << " Parent scope: " << scopeF->getName() << "\n");
572591
assert(visited.count(scopeF));
592+
// Closures must be defined in the same module as their parent scope.
593+
assert(scopeF->wasDeserializedCanonical()
594+
== F->wasDeserializedCanonical());
573595
}
574596
}
575597
visited.insert(F);
576598
#endif
599+
// Deserialized functions, which have been mandatory inlined, no longer meet
600+
// the structural requirements on access markers required by this pass.
601+
if (F->wasDeserializedCanonical())
602+
return;
577603

578604
for (auto &bb : *F) {
579605
for (auto ii = bb.begin(), ie = bb.end(); ii != ie;) {

test/SILOptimizer/access_enforcement_selection.sil

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,36 @@ bb0(%0 : @trivial $*Builtin.Int64, %1 : @trivial $Builtin.Int64):
228228
%closure = partial_apply %f(%0) : $@convention(thin) (@inout_aliasable Builtin.Int64) -> ()
229229
unreachable
230230
}
231+
232+
sil [canonical] @serializedClosureCapturingByStorageAddress : $@convention(thin) (@inout_aliasable Builtin.Int64) -> () {
233+
bb0(%0 : @trivial $*Builtin.Int64):
234+
%2 = begin_access [read] [unknown] %0 : $*Builtin.Int64
235+
%3 = load [trivial] %2 : $*Builtin.Int64
236+
end_access %2 : $*Builtin.Int64
237+
%10 = tuple ()
238+
return %10 : $()
239+
}
240+
241+
// A begin_access may not be used by a partial_apply or a nested
242+
// begin_access prior to mandatory inlining. Nonetheless, this does
243+
// occur in deserialzied SIL. This SIL is only well-formed because the
244+
// function is marked [canonical].
245+
sil [canonical] @accessAroundClosure : $@convention(thin) () -> () {
246+
bb0:
247+
%1 = alloc_box ${ var Builtin.Int64 }, var, name "x"
248+
%2 = copy_value %1 : ${ var Builtin.Int64 }
249+
%3 = project_box %1 : ${ var Builtin.Int64 }, 0
250+
// outer access (presumably its uses were mandatory inlined)
251+
%4 = begin_access [modify] [static] %3 : $*Builtin.Int64
252+
%5 = function_ref @serializedClosureCapturingByStorageAddress : $@convention(thin) (@inout_aliasable Builtin.Int64) -> ()
253+
%6 = partial_apply %5(%4) : $@convention(thin) (@inout_aliasable Builtin.Int64) -> ()
254+
%7 = begin_access [modify] [static] %4 : $*Builtin.Int64
255+
%8 = function_ref @takesInoutAndClosure : $@convention(thin) (@inout Builtin.Int64, @owned @callee_owned () -> ()) -> ()
256+
%9 = apply %8(%7, %6) : $@convention(thin) (@inout Builtin.Int64, @owned @callee_owned () -> ()) -> ()
257+
end_access %7 : $*Builtin.Int64
258+
end_access %4 : $*Builtin.Int64
259+
destroy_value %2 : ${ var Builtin.Int64 }
260+
destroy_value %1 : ${ var Builtin.Int64 }
261+
%10 = tuple ()
262+
return %10 : $()
263+
}

0 commit comments

Comments
 (0)