Skip to content

Commit 9d3b6be

Browse files
authored
Merge pull request #16003 from atrick/access-analysis
2 parents 4198f28 + b66fa09 commit 9d3b6be

16 files changed

+1103
-184
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ namespace swift {
2424

2525
// stripAddressAccess() is declared in InstructionUtils.h.
2626

27+
inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) {
28+
return !(a == SILAccessKind::Read && b == SILAccessKind::Read);
29+
}
30+
2731
/// Represents the identity of a stored class property as a combination
2832
/// of a base and a single projection. Eventually the goal is to make this
2933
/// more precise and consider, casts, etc.
@@ -88,6 +92,7 @@ class AccessedStorage {
8892
enum Kind {
8993
Box, Stack, Global, Class, Argument, Nested, Unidentified
9094
};
95+
static const char *getKindName(Kind k);
9196

9297
/// If the given address source is an identified access base, return the kind
9398
/// of access base. Otherwise, return Unidentified.
@@ -108,6 +113,9 @@ class AccessedStorage {
108113

109114
AccessedStorage(SILValue base, Kind kind);
110115

116+
AccessedStorage(SILValue object, Projection projection)
117+
: kind(Class), objProj(object, projection) {}
118+
111119
// Return true if this is a valid storage location.
112120
operator bool() const {
113121
return kind != Unidentified || value;
@@ -186,6 +194,9 @@ class AccessedStorage {
186194
/// determined. Otherwise returns null. For diagnostics and checking via the
187195
/// ValueDecl if we are processing a `let` variable.
188196
const ValueDecl *getDecl(SILFunction *F) const;
197+
198+
void print(raw_ostream &os) const;
199+
void dump() const;
189200
};
190201
} // end namespace swift
191202

@@ -288,6 +299,10 @@ bool memInstMustInitialize(Operand *memOper);
288299
/// a begin_access marker. To determine whether to emit begin_access:
289300
/// storage = findAccessedStorage(address)
290301
/// needsAccessMarker = storage && isPossibleFormalAccessBase(storage)
302+
///
303+
/// Warning: This is only valid for SIL with well-formed accessed. For example,
304+
/// it will not handle address-type phis. Optimization passes after
305+
/// DiagnoseStaticExclusivity may violate these assumptions.
291306
bool isPossibleFormalAccessBase(const AccessedStorage &storage, SILFunction *F);
292307

293308
/// Visit each address accessed by the given memory operation.

include/swift/SIL/Projection.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,8 @@ class Projection {
474474
createAggFromFirstLevelProjections(SILBuilder &B, SILLocation Loc,
475475
SILType BaseType,
476476
llvm::SmallVectorImpl<SILValue> &Values);
477+
478+
void print(raw_ostream &os, SILType baseType) const;
477479
private:
478480
/// Convenience method for getting the raw underlying index as a pointer.
479481
TypeBase *getPointer() const {
@@ -685,8 +687,8 @@ class ProjectionPath {
685687

686688
void verify(SILModule &M);
687689

688-
raw_ostream &print(raw_ostream &OS, SILModule &M);
689-
void dump(SILModule &M);
690+
raw_ostream &print(raw_ostream &OS, SILModule &M) const;
691+
void dump(SILModule &M) const;
690692
};
691693

692694
/// Returns the hashcode for the new projection path.
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
//===--- AccessedStorageAnalysis.h - Accessed Storage Analysis --*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 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 file implements an interprocedural analysis pass that summarizes the
14+
// dynamically enforced formal accesses within a function. These summaries are
15+
// used by AccessEnforcementOpts to locally fold access scopes and remove
16+
// dynamic checks based on whole module analysis.
17+
//
18+
// Note: This can be easily augmented to simultaneously compute
19+
// FunctionSideEffects by adding FunctionSideEffects as a member of
20+
// FunctionAccessedStorage. However, currently, the only use of
21+
// FunctionAccessedStorage is local to summarizeFunction().
22+
//
23+
//===----------------------------------------------------------------------===//
24+
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ACCESSED_STORAGE_ANALYSIS_H_
25+
#define SWIFT_SILOPTIMIZER_ANALYSIS_ACCESSED_STORAGE_ANALYSIS_H_
26+
27+
#include "swift/SIL/MemAccessUtils.h"
28+
#include "swift/SIL/SILFunction.h"
29+
#include "swift/SIL/SILInstruction.h"
30+
#include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h"
31+
32+
namespace swift {
33+
34+
/// Information about a formal access within a function pertaining to a
35+
/// particular AccessedStorage location.
36+
struct StorageAccessInfo {
37+
SILAccessKind accessKind;
38+
bool noNestedConflict = false;
39+
40+
StorageAccessInfo() {}
41+
42+
template <typename B>
43+
explicit StorageAccessInfo(B *beginAccess)
44+
: accessKind(beginAccess->getAccessKind()),
45+
noNestedConflict(beginAccess->hasNoNestedConflict()) {}
46+
47+
bool mergeFrom(const StorageAccessInfo &RHS);
48+
};
49+
50+
/// The per-function result of AccessedStorageAnalysis.
51+
///
52+
/// Maps each unique AccessedStorage location to StorageAccessInfo.
53+
///
54+
/// Any unidentified accesses are summarized as a single unidentifiedAccess
55+
/// property.
56+
class FunctionAccessedStorage {
57+
using AccessedStorageMap =
58+
llvm::SmallDenseMap<AccessedStorage, StorageAccessInfo, 8>;
59+
60+
AccessedStorageMap storageAccessMap;
61+
Optional<SILAccessKind> unidentifiedAccess;
62+
63+
public:
64+
FunctionAccessedStorage() {}
65+
66+
void clear() {
67+
storageAccessMap.clear();
68+
unidentifiedAccess = None;
69+
}
70+
71+
/// Sets the most conservative effects, if we don't know anything about the
72+
/// function.
73+
void setWorstEffects() {
74+
storageAccessMap.clear();
75+
unidentifiedAccess = SILAccessKind::Modify;
76+
}
77+
78+
/// Summarize the given function's effects using this FunctionAccessedStorage
79+
/// object.
80+
//
81+
// Return true if the function's' effects have been fully summarized without
82+
// visiting it's body.
83+
bool summarizeFunction(SILFunction *F);
84+
85+
/// Summarize the callee side effects of a call instruction using this
86+
/// FunctionAccessedStorage object without analyzing the callee function
87+
/// bodies or scheduling the callees for bottom-up propagation.
88+
///
89+
/// The side effects are represented from the callee's perspective. Parameter
90+
/// effects are not translated into information on the caller's argument, and
91+
/// local effects are not dropped.
92+
///
93+
/// Return true if this call-site's effects are summarized without visiting
94+
/// the callee.
95+
///
96+
/// TODO: Summarize ArraySemanticsCall accesses.
97+
bool summarizeCall(FullApplySite fullApply) {
98+
assert(storageAccessMap.empty() && "expected uninitialized results.");
99+
return false;
100+
}
101+
102+
/// Merge effects directly from \p RHS.
103+
bool mergeFrom(const FunctionAccessedStorage &RHS);
104+
105+
/// Merge the effects represented in calleeAccess into this
106+
/// FunctionAccessedStorage object. calleeAccess must correspond to at least
107+
/// one callee at the apply site `fullApply`. Merging drops any local effects,
108+
/// and translates parameter effects into effects on the caller-side
109+
/// arguments.
110+
///
111+
/// The full caller-side effects at a call site can be obtained with
112+
/// AccessedStorageAnalysis::getCallSiteEffects().
113+
bool mergeFromApply(const FunctionAccessedStorage &calleeAccess,
114+
FullApplySite fullApply);
115+
116+
/// Analyze the side-effects of a single SIL instruction \p I.
117+
/// Visited callees are added to \p BottomUpOrder until \p RecursionDepth
118+
/// reaches MaxRecursionDepth.
119+
void analyzeInstruction(SILInstruction *I);
120+
121+
/// Does any of the accesses represented by this FunctionAccessedStorage
122+
/// object conflict with the given access kind and storage.
123+
bool mayConflictWith(SILAccessKind otherAccessKind,
124+
const AccessedStorage &otherStorage);
125+
126+
void print(raw_ostream &os) const;
127+
void dump() const;
128+
129+
protected:
130+
bool updateUnidentifiedAccess(SILAccessKind accessKind);
131+
132+
bool mergeAccesses(
133+
const FunctionAccessedStorage &RHS,
134+
std::function<AccessedStorage(const AccessedStorage &)> transformStorage);
135+
136+
template <typename B> void visitBeginAccess(B *beginAccess);
137+
};
138+
139+
/// Summarizes the dynamic accesses performed within a function and its
140+
/// callees.
141+
///
142+
/// When summarizing multiple accesses, a Read access indicates that all
143+
/// accesses are read only. A Write access indicates potential reads and writes.
144+
///
145+
/// Unidentified accesses are not recorded individually. Rather, the function is
146+
/// marked as potentially executing unidentified reads or writes. An incomplete
147+
/// function, without a known callee set, is considered to have unidentified
148+
/// writes.
149+
class AccessedStorageAnalysis
150+
: public GenericFunctionEffectAnalysis<FunctionAccessedStorage> {
151+
public:
152+
AccessedStorageAnalysis()
153+
: GenericFunctionEffectAnalysis<FunctionAccessedStorage>(
154+
AnalysisKind::AccessedStorage) {}
155+
156+
static bool classof(const SILAnalysis *S) {
157+
return S->getKind() == AnalysisKind::AccessedStorage;
158+
}
159+
};
160+
161+
} // end namespace swift
162+
163+
#endif // SWIFT_SILOPTIMIZER_ANALYSIS_ACCESSED_STORAGE_ANALYSIS_H_

include/swift/SILOptimizer/Analysis/Analysis.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#endif
2525

2626
ANALYSIS(AccessSummary)
27+
ANALYSIS(AccessedStorage)
2728
ANALYSIS(Alias)
2829
ANALYSIS(BasicCallee)
2930
ANALYSIS(Caller)

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ PASS(AccessEnforcementSelection, "access-enforcement-selection",
5858
"Access Enforcement Selection")
5959
PASS(AccessSummaryDumper, "access-summary-dump",
6060
"Dump Address Parameter Access Summary")
61+
PASS(AccessedStorageDumper, "accessed-storage-dump",
62+
"Dump Accessed Storage Summary")
6163
PASS(AccessMarkerElimination, "access-marker-elim",
6264
"Access Marker Elimination.")
6365
PASS(AddressLowering, "address-lowering",

lib/SIL/MemAccessUtils.cpp

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,53 @@ const ValueDecl *AccessedStorage::getDecl(SILFunction *F) const {
105105
}
106106
}
107107

108-
// An address base is a block argument. Verify that it is actually a box
109-
// projected from a switch_enum.
108+
const char *AccessedStorage::getKindName(AccessedStorage::Kind k) {
109+
switch (k) {
110+
case Box:
111+
return "Box";
112+
case Stack:
113+
return "Stack";
114+
case Nested:
115+
return "Nested";
116+
case Unidentified:
117+
return "Unidentified";
118+
case Argument:
119+
return "Argument";
120+
case Global:
121+
return "Global";
122+
case Class:
123+
return "Class";
124+
}
125+
}
126+
127+
void AccessedStorage::print(raw_ostream &os) const {
128+
os << getKindName(kind) << " ";
129+
switch (kind) {
130+
case Box:
131+
case Stack:
132+
case Nested:
133+
case Unidentified:
134+
os << value;
135+
break;
136+
case Argument:
137+
os << "index: " << paramIndex << "\n";
138+
break;
139+
case Global:
140+
os << *global;
141+
break;
142+
case Class:
143+
os << objProj.getObject() << " ";
144+
objProj.getProjection().print(os, objProj.getObject()->getType());
145+
os << "\n";
146+
}
147+
}
148+
149+
void AccessedStorage::dump() const { print(llvm::dbgs()); }
150+
151+
// Given an address base is a block argument, verify that it is actually a box
152+
// projected from a switch_enum. This is a valid pattern at any SIL stage
153+
// resulting in a block-type phi. In later SIL stages, the optimizer may form
154+
// address-type phis, causing this assert if called on those cases.
110155
static void checkSwitchEnumBlockArg(SILPHIArgument *arg) {
111156
assert(!arg->getType().isAddress());
112157
SILBasicBlock *Pred = arg->getParent()->getSinglePredecessorBlock();
@@ -163,6 +208,9 @@ AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) {
163208
// A block argument may be a box value projected out of
164209
// switch_enum. Address-type block arguments are not allowed.
165210
case ValueKind::SILPHIArgument:
211+
if (address->getType().isAddress())
212+
return AccessedStorage();
213+
166214
checkSwitchEnumBlockArg(cast<SILPHIArgument>(address));
167215
return AccessedStorage(address, AccessedStorage::Unidentified);
168216

@@ -318,7 +366,7 @@ bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage,
318366
if (isScratchBuffer(storage.getValue()))
319367
return false;
320368
}
321-
// Additional checks that apply to anything that may fal through.
369+
// Additional checks that apply to anything that may fall through.
322370

323371
// Immutable values are only accessed for initialization.
324372
if (isLetAccess(storage, F))

0 commit comments

Comments
 (0)