Skip to content

Commit 86d6048

Browse files
committed
---
yaml --- r: 344935 b: refs/heads/master c: 1f29048 h: refs/heads/master i: 344933: ed08f59 344931: 362ebd4 344927: 4f6b4cf
1 parent 6d98777 commit 86d6048

File tree

5 files changed

+179
-50
lines changed

5 files changed

+179
-50
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
refs/heads/master: 74026498a0dcd0c34032c97f7a789358f7059879
2+
refs/heads/master: 1f29048142c7a26e86223ba3877bd79f41a32c13
33
refs/heads/master-next: 203b3026584ecad859eb328b2e12490099409cd5
44
refs/tags/osx-passed: b6b74147ef8a386f532cf9357a1bde006e552c54
55
refs/tags/swift-2.2-SNAPSHOT-2015-12-01-a: 6bb18e013c2284f2b45f5f84f2df2887dc0f7dea

trunk/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "swift/SIL/SILFunction.h"
3737
#include "swift/SIL/SILUndef.h"
3838
#include "swift/SILOptimizer/Analysis/ClosureScope.h"
39+
#include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
3940
#include "swift/SILOptimizer/PassManager/Transforms.h"
4041

4142
using namespace swift;
@@ -68,7 +69,6 @@ struct AddressCapture {
6869

6970
AddressCapture(Operand &oper)
7071
: site(oper.getUser()), calleeArgIdx(site.getCalleeArgIndex(oper)) {
71-
assert(isa<PartialApplyInst>(site));
7272
if (site.getOrigCalleeConv().getSILArgumentConvention(calleeArgIdx)
7373
!= SILArgumentConvention::Indirect_InoutAliasable) {
7474
site = ApplySite();
@@ -272,8 +272,16 @@ void SelectEnforcement::analyzeProjection(ProjectBoxInst *projection) {
272272

273273
continue;
274274
}
275-
if (isa<PartialApplyInst>(user))
276-
Captures.emplace_back(AddressCapture(*use));
275+
// Handle both partial applies and directly applied non-escaping closures.
276+
if (ApplySite::isa(user)) {
277+
AddressCapture capture(*use);
278+
if (capture.isValid())
279+
Captures.emplace_back(capture);
280+
else
281+
// Only full apply sites can have non-inout_aliasable address arguments,
282+
// but those aren't actually captures.
283+
assert(FullApplySite::isa(user));
284+
}
277285
}
278286
}
279287

@@ -463,6 +471,13 @@ void SelectEnforcement::updateCapture(AddressCapture capture) {
463471
if (hasPotentiallyEscapedAt(user))
464472
dynamicCaptures.recordCapture(capture);
465473
};
474+
SingleValueInstruction *PAIUser = dyn_cast<PartialApplyInst>(capture.site);
475+
if (!PAIUser) {
476+
// This is a full apply site. Immediately record the capture and return.
477+
captureIfEscaped(capture.site.getInstruction());
478+
return;
479+
}
480+
// For partial applies, check all use points of the closure.
466481
llvm::SmallSetVector<SingleValueInstruction *, 8> worklist;
467482
auto visitUse = [&](Operand *oper) {
468483
auto *user = oper->getUser();
@@ -513,7 +528,6 @@ void SelectEnforcement::updateCapture(AddressCapture capture) {
513528
captureIfEscaped(user);
514529
}
515530
};
516-
SingleValueInstruction *PAIUser = dyn_cast<PartialApplyInst>(capture.site);
517531
while (true) {
518532
for (auto *oper : PAIUser->getUses())
519533
visitUse(oper);
@@ -553,11 +567,10 @@ class AccessEnforcementSelection : public SILModuleTransform {
553567
// they reference.
554568
DynamicCaptures dynamicCaptures;
555569

556-
// Per-function book-keeping. A box is processed the first time one of it's
557-
// accesses is handled. Don't process it again for subsequent accesses.
558-
llvm::DenseSet<AllocBoxInst *> handledBoxes;
559-
560570
#ifndef NDEBUG
571+
// Per-function book-keeping to verify that a box is processed before all of
572+
// its accesses and captures are seen.
573+
llvm::DenseSet<AllocBoxInst *> handledBoxes;
561574
llvm::DenseSet<SILFunction *> visited;
562575
#endif
563576

@@ -568,7 +581,7 @@ class AccessEnforcementSelection : public SILModuleTransform {
568581
void processFunction(SILFunction *F);
569582
SourceAccess getAccessKindForBox(ProjectBoxInst *projection);
570583
SourceAccess getSourceAccess(SILValue address);
571-
void handlePartialApply(PartialApplyInst *PAI);
584+
void handleApply(ApplySite apply);
572585
void handleAccess(BeginAccessInst *access);
573586
};
574587

@@ -580,6 +593,9 @@ void AccessEnforcementSelection::run() {
580593
}
581594

582595
void AccessEnforcementSelection::processFunction(SILFunction *F) {
596+
if (F->isExternalDeclaration())
597+
return;
598+
583599
LLVM_DEBUG(llvm::dbgs() << "Access Enforcement Selection in " << F->getName()
584600
<< "\n");
585601

@@ -599,29 +615,43 @@ void AccessEnforcementSelection::processFunction(SILFunction *F) {
599615
}
600616
visited.insert(F);
601617
#endif
618+
602619
// Deserialized functions, which have been mandatory inlined, no longer meet
603620
// the structural requirements on access markers required by this pass.
604621
if (F->wasDeserializedCanonical())
605622
return;
606623

607-
for (auto &bb : *F) {
608-
for (auto ii = bb.begin(), ie = bb.end(); ii != ie;) {
624+
// Perform an RPO walk so that boxes are always processed before their access.
625+
auto *PO = getAnalysis<PostOrderAnalysis>()->get(F);
626+
for (SILBasicBlock *bb : PO->getReversePostOrder()) {
627+
for (auto ii = bb->begin(), ie = bb->end(); ii != ie;) {
609628
SILInstruction *inst = &*ii;
610629
++ii;
611630

612-
if (auto access = dyn_cast<BeginAccessInst>(inst))
631+
// Analyze all boxes. Even if they aren't accessed in this function, they
632+
// may still have captures that require dynamic enforcement because the
633+
// box has escaped prior to the capture.
634+
if (auto box = dyn_cast<AllocBoxInst>(inst)) {
635+
SelectEnforcement(dynamicCaptures, box).run();
636+
assert(handledBoxes.insert(box).second);
637+
638+
} else if (auto access = dyn_cast<BeginAccessInst>(inst))
613639
handleAccess(access);
614640

615641
else if (auto access = dyn_cast<BeginUnpairedAccessInst>(inst))
616642
assert(access->getEnforcement() == SILAccessEnforcement::Dynamic);
617643

618-
else if(auto pa = dyn_cast<PartialApplyInst>(inst))
619-
handlePartialApply(pa);
644+
// Check for unboxed captures in both partial_applies and direct
645+
// applications of non-escaping closures.
646+
else if (auto apply = ApplySite::isa(inst))
647+
handleApply(apply);
620648
}
621649
}
622650
invalidateAnalysis(F, SILAnalysis::InvalidationKind::Instructions);
651+
#ifndef NDEBUG
623652
// There's no need to track handled boxes across functions.
624653
handledBoxes.clear();
654+
#endif
625655
}
626656

627657
SourceAccess
@@ -690,19 +720,17 @@ SourceAccess AccessEnforcementSelection::getSourceAccess(SILValue address) {
690720
return SourceAccess::getStaticAccess();
691721
}
692722

693-
void AccessEnforcementSelection::handlePartialApply(PartialApplyInst *PAI) {
694-
ApplySite site(PAI);
695-
auto calleeTy = PAI->getOrigCalleeType();
723+
void AccessEnforcementSelection::handleApply(ApplySite apply) {
724+
auto calleeTy = apply.getOrigCalleeType();
696725
SILFunctionConventions calleeConv(calleeTy, *getModule());
697726

698-
for (Operand &oper : site.getArgumentOperands()) {
727+
for (Operand &oper : apply.getArgumentOperands()) {
699728
AddressCapture capture(oper);
700729
if (!capture.isValid())
701730
continue;
702731

703-
// This partial apply creates a non-escaping closure. Check if the closure
704-
// captures any Boxed variables from this scope. If so, check if the box
705-
// escapes before the access just as we do for normal accesses.
732+
// This is a non-escaping closure argument. If the argument requires dynamic
733+
// access, record that in dynamicCaptures.
706734
auto sourceAccess = getSourceAccess(oper.get());
707735
switch (sourceAccess.kind) {
708736
case SourceAccess::StaticAccess:
@@ -714,8 +742,11 @@ void AccessEnforcementSelection::handlePartialApply(PartialApplyInst *PAI) {
714742
break;
715743
}
716744
case SourceAccess::BoxAccess:
717-
if (handledBoxes.insert(sourceAccess.allocBox).second)
718-
SelectEnforcement(dynamicCaptures, sourceAccess.allocBox).run();
745+
// Captures of box projections are handled during SelectEnforcement, which
746+
// determines the access enforcement for all users of a box. Within
747+
// SelectEnforcement, we know whether the box has escaped before the
748+
// capture. Here there's just nothing to do.
749+
assert(handledBoxes.count(sourceAccess.allocBox));
719750
break;
720751
}
721752
}
@@ -734,10 +765,7 @@ void AccessEnforcementSelection::handleAccess(BeginAccessInst *access) {
734765
setDynamicEnforcement(access);
735766
break;
736767
case SourceAccess::BoxAccess:
737-
// If this box was handled, the access enforcement would already be set.
738-
assert(!handledBoxes.count(sourceAccess.allocBox));
739-
SelectEnforcement(dynamicCaptures, sourceAccess.allocBox).run();
740-
break;
768+
llvm_unreachable("All boxes must have already been selected.");
741769
}
742770
}
743771

trunk/test/IRGen/partial_apply.sil

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ bb0(%0 : $Base):
360360
return %5 : $()
361361
}
362362

363-
sil public_external @partial_empty_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout ()) -> ()
363+
sil public_external @partial_empty_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout_aliasable ()) -> ()
364364

365365
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @empty_box()
366366
sil @empty_box : $@convention(thin) () -> () {
@@ -370,8 +370,8 @@ entry:
370370
// CHECK: store %swift.opaque* undef
371371
%b = alloc_box $<τ_0_0> { var τ_0_0 } <()>
372372
%ba = project_box %b : $<τ_0_0> { var τ_0_0 } <()>, 0
373-
%f = function_ref @partial_empty_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout ()) -> ()
374-
%g = partial_apply %f(%b, %ba) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout ()) -> ()
373+
%f = function_ref @partial_empty_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout_aliasable ()) -> ()
374+
%g = partial_apply %f(%b, %ba) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout_aliasable ()) -> ()
375375
return undef : $()
376376
}
377377

trunk/test/Interpreter/enforce_exclusive_access.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ func modifyAndPerform<T>(_ _: UnsafeMutablePointer<T>, closure: () ->()) {
2626
closure()
2727
}
2828

29+
/// Begin a modify access to the first parameter and call the escaping closure inside it.
30+
func modifyAndPerformEscaping<T>(_ _: UnsafeMutablePointer<T>, closure: () ->()) {
31+
closure()
32+
}
33+
2934
var globalX = X()
3035

3136
var ExclusiveAccessTestSuite = TestSuite("ExclusiveAccess")
@@ -344,4 +349,35 @@ ExclusiveAccessTestSuite.test("KeyPathInoutKeyPathReadClassStoredProp")
344349
}
345350
}
346351

352+
// <rdar://problem/43076947> [Exclusivity] improve diagnostics for
353+
// withoutActuallyEscaping.
354+
ExclusiveAccessTestSuite.test("withoutActuallyEscapingConflict") {
355+
var localVal = 0
356+
let nestedModify = { localVal = 3 }
357+
withoutActuallyEscaping(nestedModify) {
358+
expectCrashLater()
359+
modifyAndPerform(&localVal, closure: $0)
360+
}
361+
}
362+
363+
ExclusiveAccessTestSuite.test("directlyAppliedConflict") {
364+
var localVal = 0
365+
let nestedModify = { localVal = 3 }
366+
expectCrashLater()
367+
_ = {
368+
modifyAndPerform(&localVal, closure: nestedModify)
369+
}()
370+
}
371+
372+
// <rdar://problem/43122715> [Exclusivity] failure to diagnose
373+
// escaping closures called within directly applied noescape closures.
374+
ExclusiveAccessTestSuite.test("directlyAppliedEscapingConflict") {
375+
var localVal = 0
376+
let nestedModify = { localVal = 3 }
377+
expectCrashLater()
378+
_ = {
379+
modifyAndPerformEscaping(&localVal, closure: nestedModify)
380+
}()
381+
}
382+
347383
runAllTests()

0 commit comments

Comments
 (0)