Skip to content

Commit 9e35ac5

Browse files
authored
Merge pull request swiftlang#8810 from atrick/access
2 parents e5164bd + 3fe16ac commit 9e35ac5

File tree

2 files changed

+96
-24
lines changed

2 files changed

+96
-24
lines changed

lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,12 @@ class SelectEnforcement {
8787
void propagateEscapesFrom(SILBasicBlock *bb);
8888

8989
bool hasPotentiallyEscapedAt(SILInstruction *inst);
90-
bool hasPotentiallyEscapedAtAnyReachableBlock(BeginAccessInst *access);
90+
91+
typedef llvm::SmallSetVector<SILBasicBlock*, 8> BlockSetVector;
92+
void findBlocksAccessedAcross(EndAccessInst *endAccess,
93+
BlockSetVector &blocksAccessedAcross);
94+
bool hasPotentiallyEscapedAtAnyReachableBlock(
95+
BeginAccessInst *access, BlockSetVector &blocksAccessedAcross);
9196

9297
void updateAccesses();
9398
void updateAccess(BeginAccessInst *access);
@@ -216,22 +221,53 @@ bool SelectEnforcement::hasPotentiallyEscapedAt(SILInstruction *point) {
216221
return false;
217222
}
218223

219-
bool SelectEnforcement::hasPotentiallyEscapedAtAnyReachableBlock(
220-
BeginAccessInst *access) {
224+
/// Add all blocks to `Worklist` between the given `endAccess` and its
225+
/// `begin_access` in which the access is active at the end of the block.
226+
void SelectEnforcement::findBlocksAccessedAcross(
227+
EndAccessInst *endAccess, BlockSetVector &blocksAccessedAcross) {
228+
221229
// Fast path: we're not tracking any escapes. (But the box should
222230
// probably have been promoted to the stack in this case.)
223231
if (StateMap.empty())
224-
return false;
232+
return;
233+
234+
SILBasicBlock *beginBB = endAccess->getBeginAccess()->getParent();
235+
if (endAccess->getParent() == beginBB)
236+
return;
237+
238+
assert(Worklist.empty());
239+
Worklist.push_back(endAccess->getParent());
240+
while (!Worklist.empty()) {
241+
SILBasicBlock *bb = Worklist.pop_back_val();
242+
for (auto *predBB : bb->getPredecessorBlocks()) {
243+
if (!blocksAccessedAcross.insert(predBB)) continue;
244+
if (predBB == beginBB) continue;
245+
Worklist.push_back(predBB);
246+
}
247+
}
248+
}
225249

226-
auto bb = access->getParent();
250+
bool SelectEnforcement::hasPotentiallyEscapedAtAnyReachableBlock(
251+
BeginAccessInst *access, BlockSetVector &blocksAccessedAcross) {
227252

228253
assert(Worklist.empty());
229254
SmallPtrSet<SILBasicBlock*, 8> visited;
230-
visited.insert(bb);
231-
Worklist.push_back(bb);
255+
256+
// Don't follow any paths that lead to an end_access.
257+
for (auto endAccess : access->getEndAccesses())
258+
visited.insert(endAccess->getParent());
259+
260+
/// Initialize the worklist with all blocks that exit the access path.
261+
for (SILBasicBlock *bb : blocksAccessedAcross) {
262+
for (SILBasicBlock *succBB : bb->getSuccessorBlocks()) {
263+
if (blocksAccessedAcross.count(succBB)) continue;
264+
if (visited.insert(succBB).second)
265+
Worklist.push_back(succBB);
266+
}
267+
}
232268

233269
while (!Worklist.empty()) {
234-
bb = Worklist.pop_back_val();
270+
SILBasicBlock *bb = Worklist.pop_back_val();
235271
assert(visited.count(bb));
236272

237273
// If we're tracking information for this block, there's an escape.
@@ -259,27 +295,23 @@ void SelectEnforcement::updateAccess(BeginAccessInst *access) {
259295
assert(access->getEnforcement() == SILAccessEnforcement::Unknown);
260296

261297
// Check whether the variable escaped before any of the end_accesses.
262-
bool anyDynamic = false;
263-
bool hasEndAccess = false;
298+
BlockSetVector blocksAccessedAcross;
264299
for (auto endAccess : access->getEndAccesses()) {
265-
hasEndAccess = true;
266-
if (hasPotentiallyEscapedAt(endAccess)) {
267-
anyDynamic = true;
268-
break;
269-
}
300+
if (hasPotentiallyEscapedAt(endAccess))
301+
return setDynamicEnforcement(access);
302+
303+
// Add all blocks to blocksAccessedAcross between begin_access and this
304+
// end_access.
305+
findBlocksAccessedAcross(endAccess, blocksAccessedAcross);
270306
}
307+
assert(blocksAccessedAcross.empty()
308+
|| blocksAccessedAcross.count(access->getParent()));
271309

272-
// If so, make the access dynamic.
273-
if (anyDynamic)
310+
// For every path through this access that doesn't reach an end_access, check
311+
// if any block reachable from that path can see an escaped value.
312+
if (hasPotentiallyEscapedAtAnyReachableBlock(access, blocksAccessedAcross)) {
274313
return setDynamicEnforcement(access);
275-
276-
// Otherwise, if there are no end_access instructions,
277-
// check the terminators of every reachable block.
278-
if (!hasEndAccess) {
279-
if (hasPotentiallyEscapedAtAnyReachableBlock(access))
280-
return setDynamicEnforcement(access);
281314
}
282-
283315
// Otherwise, use static enforcement.
284316
setStaticEnforcement(access);
285317
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -access-enforcement-selection -enforce-exclusivity=checked | %FileCheck %s
2+
3+
sil_stage raw
4+
5+
import Builtin
6+
import Swift
7+
8+
sil @closure : $@convention(thin) (@owned { var Builtin.Int64 }) -> () {
9+
bb0(%0 : ${var Builtin.Int64}):
10+
%empty = tuple ()
11+
return %empty : $()
12+
}
13+
14+
// An access that escapes on an unreachable path must be dynamic.
15+
//
16+
// CHECK-LABEL: sil @partialUnreachable : $@convention(thin) () -> () {
17+
// CHECK: %[[ACCESS:.*]] = begin_access [modify] [dynamic] %{{.*}} : $*Builtin.Int64
18+
// CHECK: bb1:
19+
// CHECK: end_access %[[ACCESS]] : $*Builtin.Int64
20+
// CHECK: return
21+
// CHECK: bb2:
22+
// CHECK: partial_apply
23+
// CHECK: unreachable
24+
sil @partialUnreachable : $@convention(thin) () -> () {
25+
bb0:
26+
%box = alloc_box ${ var Builtin.Int64 }, var, name "x"
27+
%addr = project_box %box : ${ var Builtin.Int64 }, 0
28+
%write = begin_access [modify] [unknown] %addr : $*Builtin.Int64
29+
cond_br undef, bb1, bb2
30+
31+
bb1:
32+
end_access %write : $*Builtin.Int64
33+
%empty = tuple ()
34+
return %empty : $()
35+
36+
bb2:
37+
%f = function_ref @closure : $@convention(thin) (@owned { var Builtin.Int64 }) -> ()
38+
%closure = partial_apply %f(%box) : $@convention(thin) (@owned { var Builtin.Int64 }) -> ()
39+
unreachable
40+
}

0 commit comments

Comments
 (0)