Skip to content

Commit 6956926

Browse files
authored
Merge pull request #8686 from rjmccall/access-enforcement-selection-pass
2 parents 21a0218 + b9676d2 commit 6956926

File tree

6 files changed

+376
-1
lines changed

6 files changed

+376
-1
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,6 +1907,17 @@ class BeginAccessInst
19071907
SILValue getSource() const {
19081908
return getOperand();
19091909
}
1910+
1911+
private:
1912+
/// Predicate used to filter EndAccessRange.
1913+
struct UseToEndAccess;
1914+
1915+
public:
1916+
using EndAccessRange =
1917+
OptionalTransformRange<use_range, UseToEndAccess, use_iterator>;
1918+
1919+
/// Find all the associated end_access instructions for this begin_access.
1920+
EndAccessRange getEndAccesses() const;
19101921
};
19111922

19121923
/// Represents the end of an access scope.
@@ -1944,6 +1955,20 @@ class EndAccessInst : public UnaryInstructionBase<ValueKind::EndAccessInst> {
19441955
}
19451956
};
19461957

1958+
struct BeginAccessInst::UseToEndAccess {
1959+
Optional<EndAccessInst *> operator()(Operand *use) const {
1960+
if (auto access = dyn_cast<EndAccessInst>(use->getUser())) {
1961+
return access;
1962+
} else {
1963+
return None;
1964+
}
1965+
}
1966+
};
1967+
1968+
inline auto BeginAccessInst::getEndAccesses() const -> EndAccessRange {
1969+
return EndAccessRange(getUses(), UseToEndAccess());
1970+
}
1971+
19471972
/// AssignInst - Represents an abstract assignment to a memory location, which
19481973
/// may either be an initialization or a store sequence. This is only valid in
19491974
/// Raw SIL.

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ PASS(AADumper, "aa-dump",
5454
"Dump Alias Analysis over all Pairs")
5555
PASS(ABCOpt, "abcopts",
5656
"Array Bounds Check Optimization")
57+
PASS(AccessEnforcementSelection, "access-enforcement-selection",
58+
"Access Enforcement Selection")
5759
PASS(AccessMarkerElimination, "access-marker-elim",
5860
"Access Marker Elimination.")
5961
PASS(AddressLowering, "address-lowering",
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
//===--- AccessEnforcementSelection.cpp - Select access enforcement -------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
///
13+
/// This pass eliminates 'unknown' access enforcement by selecting either
14+
/// static or dynamic enforcement.
15+
///
16+
//===----------------------------------------------------------------------===//
17+
18+
#define DEBUG_TYPE "access-enforcement-selection"
19+
#include "swift/SIL/SILFunction.h"
20+
#include "swift/SILOptimizer/PassManager/Transforms.h"
21+
22+
using namespace swift;
23+
24+
static void setStaticEnforcement(BeginAccessInst *access) {
25+
// TODO: delete if we're not using static enforcement?
26+
access->setEnforcement(SILAccessEnforcement::Static);
27+
}
28+
29+
static void setDynamicEnforcement(BeginAccessInst *access) {
30+
// TODO: delete if we're not using dynamic enforcement?
31+
access->setEnforcement(SILAccessEnforcement::Dynamic);
32+
}
33+
34+
namespace {
35+
class SelectEnforcement {
36+
AllocBoxInst *Box;
37+
38+
/// A state for tracking escape information about a variable.
39+
/// StateMap only has entries for blocks for which the variable
40+
/// has potentially escaped at exit.
41+
struct State {
42+
bool IsInWorklist = false;
43+
44+
// At least one of the following must be true.
45+
bool HasEscape = false;
46+
bool HasPotentiallyEscapedAtEntry = false;
47+
48+
// In a more advanced problem, this could easily be passed a State.
49+
bool adjustForEscapeInPredecessor() {
50+
bool updateSuccessors = false;
51+
52+
if (!HasPotentiallyEscapedAtEntry) {
53+
HasPotentiallyEscapedAtEntry = true;
54+
updateSuccessors = !HasEscape;
55+
}
56+
57+
return updateSuccessors;
58+
}
59+
};
60+
llvm::DenseMap<SILBasicBlock*, State> StateMap;
61+
62+
/// All the accesses in the function.
63+
SmallVector<BeginAccessInst*, 8> Accesses;
64+
65+
/// All the escapes in the function.
66+
SmallPtrSet<SILInstruction*, 8> Escapes;
67+
68+
/// A worklist we use for various purposes.
69+
SmallVector<SILBasicBlock*, 8> Worklist;
70+
71+
public:
72+
SelectEnforcement(AllocBoxInst *box) : Box(box) {}
73+
74+
void run();
75+
76+
private:
77+
void analyzeUsesOfBox();
78+
void analyzeProjection(ProjectBoxInst *projection);
79+
80+
void propagateEscapes();
81+
void propagateEscapesFrom(SILBasicBlock *bb);
82+
83+
bool hasPotentiallyEscapedAt(SILInstruction *inst);
84+
bool hasPotentiallyEscapedAtAnyReachableBlock(BeginAccessInst *access);
85+
86+
void updateAccesses();
87+
void updateAccess(BeginAccessInst *access);
88+
};
89+
} // end anonymous namespace
90+
91+
void SelectEnforcement::run() {
92+
// Set up the data-flow problem.
93+
analyzeUsesOfBox();
94+
95+
// Run the data-flow problem.
96+
propagateEscapes();
97+
98+
// Update all the accesses.
99+
updateAccesses();
100+
}
101+
102+
void SelectEnforcement::analyzeUsesOfBox() {
103+
// Collect accesses rooted off of projections.
104+
for (auto use : Box->getUses()) {
105+
auto user = use->getUser();
106+
107+
if (auto projection = dyn_cast<ProjectBoxInst>(user)) {
108+
analyzeProjection(projection);
109+
continue;
110+
}
111+
112+
// Ignore certain other uses that do not capture the value.
113+
if (isa<StrongRetainInst>(user) ||
114+
isa<StrongReleaseInst>(user) ||
115+
isa<DestroyValueInst>(user) ||
116+
isa<DeallocBoxInst>(user))
117+
continue;
118+
119+
// Treat everything else as an escape:
120+
121+
// Add it to the escapes set.
122+
Escapes.insert(user);
123+
124+
//
125+
auto userBB = user->getParent();
126+
auto &state = StateMap[userBB];
127+
if (!state.IsInWorklist) {
128+
state.HasEscape = true;
129+
state.IsInWorklist = true;
130+
Worklist.push_back(userBB);
131+
}
132+
assert(state.HasEscape);
133+
assert(state.IsInWorklist);
134+
}
135+
136+
assert(!Accesses.empty() && "didn't find original access!");
137+
}
138+
139+
void SelectEnforcement::analyzeProjection(ProjectBoxInst *projection) {
140+
for (auto use : projection->getUses()) {
141+
auto user = use->getUser();
142+
143+
// Collect accesses.
144+
if (auto access = dyn_cast<BeginAccessInst>(user)) {
145+
if (access->getEnforcement() == SILAccessEnforcement::Unknown)
146+
Accesses.push_back(access);
147+
}
148+
}
149+
}
150+
151+
void SelectEnforcement::propagateEscapes() {
152+
while (!Worklist.empty()) {
153+
auto bb = Worklist.pop_back_val();
154+
auto it = StateMap.find(bb);
155+
assert(it != StateMap.end() &&
156+
"block was in worklist but doesn't have a tracking state");
157+
auto &state = it->second;
158+
assert(state.HasEscape || state.HasPotentiallyEscapedAtEntry);
159+
state.IsInWorklist = false;
160+
propagateEscapesFrom(bb);
161+
}
162+
}
163+
164+
/// Given that the box potentially escaped before we exited the
165+
/// given block, propagate that information to all of its successors.
166+
void SelectEnforcement::propagateEscapesFrom(SILBasicBlock *bb) {
167+
assert(StateMap.count(bb));
168+
169+
// Iterate over the successors of the block.
170+
for (SILBasicBlock *succ : bb->getSuccessors()) {
171+
auto &succState = StateMap[succ];
172+
173+
// If updating the successor changes it in a way that will
174+
// require us to update its successors, add it to the worklist.
175+
if (succState.adjustForEscapeInPredecessor()) {
176+
if (!succState.IsInWorklist) {
177+
succState.IsInWorklist = true;
178+
Worklist.push_back(succ);
179+
}
180+
}
181+
}
182+
}
183+
184+
bool SelectEnforcement::hasPotentiallyEscapedAt(SILInstruction *point) {
185+
auto bb = point->getParent();
186+
187+
// If we're not tracking anything for the whole block containing
188+
// the instruction, we're done; it hasn't escaped here.
189+
auto it = StateMap.find(bb);
190+
if (it == StateMap.end())
191+
return false;
192+
193+
// If the tracking information says there are escapes before entry,
194+
// we're done; it has potentially escaped.
195+
const auto &state = it->second;
196+
if (state.HasPotentiallyEscapedAtEntry)
197+
return true;
198+
199+
// Okay, there must be an escape within this block.
200+
assert(state.HasEscape);
201+
for (auto ii = point->getIterator(), ie = bb->begin(); ii != ie; ) {
202+
auto inst = &*--ii;
203+
204+
// Maybe just record the first escape in the block and see if we
205+
// come after it?
206+
if (Escapes.count(inst))
207+
return true;
208+
}
209+
210+
return false;
211+
}
212+
213+
bool SelectEnforcement::hasPotentiallyEscapedAtAnyReachableBlock(
214+
BeginAccessInst *access) {
215+
// Fast path: we're not tracking any escapes. (But the box should
216+
// probably have been promoted to the stack in this case.)
217+
if (StateMap.empty())
218+
return false;
219+
220+
auto bb = access->getParent();
221+
222+
assert(Worklist.empty());
223+
SmallPtrSet<SILBasicBlock*, 8> visited;
224+
visited.insert(bb);
225+
Worklist.push_back(bb);
226+
227+
while (!Worklist.empty()) {
228+
bb = Worklist.pop_back_val();
229+
assert(visited.count(bb));
230+
231+
// If we're tracking information for this block, there's an escape.
232+
if (StateMap.count(bb))
233+
return true;
234+
235+
// Add all reachable successors.
236+
for (SILBasicBlock *succ : bb->getSuccessors()) {
237+
if (visited.insert(succ).second)
238+
Worklist.push_back(succ);
239+
}
240+
}
241+
242+
// No reachable block has an escape.
243+
return false;
244+
}
245+
246+
void SelectEnforcement::updateAccesses() {
247+
for (auto access : Accesses) {
248+
updateAccess(access);
249+
}
250+
}
251+
252+
void SelectEnforcement::updateAccess(BeginAccessInst *access) {
253+
assert(access->getEnforcement() == SILAccessEnforcement::Unknown);
254+
255+
// Check whether the variable escaped before any of the end_accesses.
256+
bool anyDynamic = false;
257+
bool hasEndAccess = false;
258+
for (auto endAccess : access->getEndAccesses()) {
259+
hasEndAccess = true;
260+
if (hasPotentiallyEscapedAt(endAccess)) {
261+
anyDynamic = true;
262+
break;
263+
}
264+
}
265+
266+
// If so, make the access dynamic.
267+
if (anyDynamic)
268+
return setDynamicEnforcement(access);
269+
270+
// Otherwise, if there are no end_access instructions,
271+
// check the terminators of every reachable block.
272+
if (!hasEndAccess) {
273+
if (hasPotentiallyEscapedAtAnyReachableBlock(access))
274+
return setDynamicEnforcement(access);
275+
}
276+
277+
// Otherwise, use static enforcement.
278+
setStaticEnforcement(access);
279+
}
280+
281+
namespace {
282+
283+
/// The pass.
284+
struct AccessEnforcementSelection : SILFunctionTransform {
285+
void run() override {
286+
for (auto &bb : *getFunction()) {
287+
for (auto ii = bb.begin(), ie = bb.end(); ii != ie; ) {
288+
SILInstruction *inst = &*ii;
289+
++ii;
290+
291+
if (auto access = dyn_cast<BeginAccessInst>(inst)) {
292+
handleAccess(access);
293+
}
294+
}
295+
}
296+
}
297+
298+
void handleAccess(BeginAccessInst *access) {
299+
if (access->getEnforcement() != SILAccessEnforcement::Unknown)
300+
return;
301+
302+
auto address = access->getOperand();
303+
assert(!isa<MarkUninitializedInst>(address) &&
304+
"pass should be run after definite initialization");
305+
306+
if (auto box = dyn_cast<ProjectBoxInst>(address)) {
307+
return handleAccessToBox(access, box);
308+
}
309+
310+
// If we're not accessing a box, we must've lowered to
311+
// a stack element.
312+
assert(isa<AllocStackInst>(address));
313+
setStaticEnforcement(access);
314+
}
315+
316+
void handleAccessToBox(BeginAccessInst *access, ProjectBoxInst *projection) {
317+
// If we didn't allocate the box, assume that we need to use
318+
// dynamic enforcement.
319+
// TODO: use static enforcement in certain provable cases.
320+
auto box = dyn_cast<AllocBoxInst>(projection->getOperand());
321+
if (!box) {
322+
setDynamicEnforcement(access);
323+
return;
324+
}
325+
326+
SelectEnforcement(box).run();
327+
}
328+
};
329+
330+
} // end anonymous namespace
331+
332+
SILTransform *swift::createAccessEnforcementSelection() {
333+
return new AccessEnforcementSelection();
334+
}

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
set(MANDATORY_SOURCES
2+
Mandatory/AccessEnforcementSelection.cpp
23
Mandatory/AccessMarkerElimination.cpp
34
Mandatory/AddressLowering.cpp
45
Mandatory/DefiniteInitialization.cpp

0 commit comments

Comments
 (0)