Skip to content

Commit beb46eb

Browse files
committed
Use the new escape and side effects in alias analysis
1 parent 814d890 commit beb46eb

35 files changed

+469
-436
lines changed

SwiftCompilerSources/Sources/Optimizer/Analysis/AliasAnalysis.swift

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,148 @@ struct AliasAnalysis {
6060
// Any other non-address value means: all addresses of any referenced class instances within the value.
6161
return SmallProjectionPath(.anyValueFields).push(.anyClassField).push(.anyValueFields)
6262
}
63+
64+
static func register() {
65+
AliasAnalysis_register(
66+
// getMemEffectsFn
67+
{ (bridgedCtxt: BridgedPassContext, bridgedVal: BridgedValue, bridgedInst: BridgedInstruction) -> BridgedMemoryBehavior in
68+
let context = PassContext(_bridged: bridgedCtxt)
69+
let inst = bridgedInst.instruction
70+
let val = bridgedVal.value
71+
let path = AliasAnalysis.getPtrOrAddressPath(for: val)
72+
if let apply = inst as? ApplySite {
73+
let effect = getMemoryEffect(of: apply, for: val, path: path, context)
74+
switch (effect.read, effect.write) {
75+
case (false, false): return NoneBehavior
76+
case (true, false): return MayReadBehavior
77+
case (false, true): return MayWriteBehavior
78+
case (true, true): return MayReadWriteBehavior
79+
}
80+
}
81+
if val.at(path).isAddressEscaping(using: EscapesToInstructionVisitor(target: inst), context) {
82+
return MayReadWriteBehavior
83+
}
84+
return NoneBehavior
85+
},
86+
87+
// isObjReleasedFn
88+
{ (bridgedCtxt: BridgedPassContext, bridgedObj: BridgedValue, bridgedInst: BridgedInstruction) -> Bool in
89+
let context = PassContext(_bridged: bridgedCtxt)
90+
let inst = bridgedInst.instruction
91+
let obj = bridgedObj.value
92+
let path = SmallProjectionPath(.anyValueFields)
93+
if let apply = inst as? ApplySite {
94+
let effect = getOwnershipEffect(of: apply, for: obj, path: path, context)
95+
return effect.destroy
96+
}
97+
return obj.at(path).isEscaping(using: EscapesToInstructionVisitor(target: inst), context)
98+
},
99+
100+
// isAddrVisibleFromObj
101+
{ (bridgedCtxt: BridgedPassContext, bridgedAddr: BridgedValue, bridgedObj: BridgedValue) -> Bool in
102+
let context = PassContext(_bridged: bridgedCtxt)
103+
let addr = bridgedAddr.value.at(AliasAnalysis.getPtrOrAddressPath(for: bridgedAddr.value))
104+
105+
// This is similar to `canReferenceSameFieldFn`, except that all addresses of all objects are
106+
// considered which are transitively visible from `bridgedObj`.
107+
let anythingReachableFromObj = bridgedObj.value.at(SmallProjectionPath(.anything))
108+
return addr.canAddressAlias(with: anythingReachableFromObj, context)
109+
},
110+
111+
// canReferenceSameFieldFn
112+
{ (bridgedCtxt: BridgedPassContext, bridgedLhs: BridgedValue, bridgedRhs: BridgedValue) -> Bool in
113+
let context = PassContext(_bridged: bridgedCtxt)
114+
115+
// If `lhs` or `rhs` is not an address, but an object, it means: check for alias of any class
116+
// field address of the object.
117+
let lhs = bridgedLhs.value.at(AliasAnalysis.getPtrOrAddressPath(for: bridgedLhs.value))
118+
let rhs = bridgedRhs.value.at(AliasAnalysis.getPtrOrAddressPath(for: bridgedRhs.value))
119+
return lhs.canAddressAlias(with: rhs, context)
120+
}
121+
)
122+
}
123+
}
124+
125+
private func getMemoryEffect(of apply: ApplySite, for address: Value, path: SmallProjectionPath, _ context: PassContext) -> SideEffects.Memory {
126+
let calleeAnalysis = context.calleeAnalysis
127+
let visitor = SideEffectsVisitor(apply: apply, calleeAnalysis: calleeAnalysis)
128+
let memoryEffects: SideEffects.Memory
129+
130+
// First try to figure out to which argument(s) the address "escapes" to.
131+
if let result = address.at(path).visitAddress(using: visitor, context) {
132+
// The resulting effects are the argument effects to which `address` escapes to.
133+
memoryEffects = result.memory
134+
} else {
135+
// `address` has unknown escapes. So we have to take the global effects of the called function(s).
136+
memoryEffects = calleeAnalysis.getSideEffects(of: apply).memory
137+
}
138+
// Do some magic for `let` variables. Function calls cannot modify let variables.
139+
// The only exception is that the let variable is directly passed to an indirect out of the
140+
// apply.
141+
// TODO: make this a more formal and verified approach.
142+
if memoryEffects.write && address.accessBase.isLet && !address.isIndirectResult(of: apply) {
143+
return SideEffects.Memory(read: memoryEffects.read, write: false)
144+
}
145+
return memoryEffects
146+
}
147+
148+
private func getOwnershipEffect(of apply: ApplySite, for value: Value, path: SmallProjectionPath, _ context: PassContext) -> SideEffects.Ownership {
149+
let visitor = SideEffectsVisitor(apply: apply, calleeAnalysis: context.calleeAnalysis)
150+
if let result = value.at(path).visit(using: visitor, context) {
151+
// The resulting effects are the argument effects to which `value` escapes to.
152+
return result.ownership
153+
} else {
154+
// `value` has unknown escapes. So we have to take the global effects of the called function(s).
155+
return visitor.calleeAnalysis.getSideEffects(of: apply).ownership
156+
}
157+
}
158+
159+
private struct SideEffectsVisitor : EscapeVisitorWithResult {
160+
let apply: ApplySite
161+
let calleeAnalysis: CalleeAnalysis
162+
var result = SideEffects.GlobalEffects()
163+
164+
mutating func visitUse(operand: Operand, path: EscapePath) -> UseResult {
165+
let user = operand.instruction
166+
if user is ReturnInst {
167+
// Anything which is returned cannot escape to an instruction inside the function.
168+
return .ignore
169+
}
170+
if user == apply {
171+
if let argIdx = apply.argumentIndex(of: operand) {
172+
let e = calleeAnalysis.getSideEffects(of: apply, forArgument: argIdx, path: path.projectionPath)
173+
result.merge(with: e)
174+
}
175+
}
176+
return .continueWalk
177+
}
178+
}
179+
180+
private extension Value {
181+
/// Returns true if this address is passed as indirect out of `apply`.
182+
func isIndirectResult(of apply: ApplySite) -> Bool {
183+
guard let fullApply = apply as? FullApplySite else {
184+
return false
185+
}
186+
if fullApply.numIndirectResultArguments == 0 {
187+
return false
188+
}
189+
190+
var walker = IsIndirectResultWalker(apply: fullApply)
191+
return walker.walkDownUses(ofAddress: self, path: UnusedWalkingPath()) == .abortWalk
192+
}
193+
}
194+
195+
private struct IsIndirectResultWalker: AddressDefUseWalker {
196+
let apply: FullApplySite
197+
198+
mutating func leafUse(address: Operand, path: UnusedWalkingPath) -> WalkResult {
199+
if address.instruction == apply,
200+
let argIdx = apply.argumentIndex(of: address),
201+
argIdx < apply.numIndirectResultArguments {
202+
return .abortWalk
203+
}
204+
return .continueWalk
205+
}
63206
}
207+

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,6 @@ private func registerSwiftPasses() {
7979
}
8080

8181
private func registerSwiftAnalyses() {
82+
AliasAnalysis.register()
8283
CalleeAnalysis.register()
8384
}

include/swift/SILOptimizer/Analysis/AliasAnalysis.h

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@
2020

2121
namespace swift {
2222

23-
class SideEffectAnalysis;
24-
class EscapeAnalysis;
25-
2623
/// This class is a simple wrapper around an alias analysis cache. This is
2724
/// needed since we do not have an "analysis" infrastructure.
2825
class AliasAnalysis {
@@ -74,8 +71,7 @@ class AliasAnalysis {
7471

7572
using TBAACacheKey = std::pair<SILType, SILType>;
7673

77-
SideEffectAnalysis *SEA;
78-
EscapeAnalysis *EA;
74+
SILPassManager *PM;
7975

8076
/// A cache for the computation of TBAA. True means that the types may
8177
/// alias. False means that the types must not alias.
@@ -125,8 +121,7 @@ class AliasAnalysis {
125121
bool isInImmutableScope(SILInstruction *inst, SILValue V);
126122

127123
public:
128-
AliasAnalysis(SideEffectAnalysis *SEA, EscapeAnalysis *EA)
129-
: SEA(SEA), EA(EA) {}
124+
AliasAnalysis(SILPassManager *PM) : PM(PM) {}
130125

131126
static SILAnalysisKind getAnalysisKind() { return SILAnalysisKind::Alias; }
132127

@@ -158,11 +153,6 @@ class AliasAnalysis {
158153
return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::MayAlias;
159154
}
160155

161-
/// \returns True if the release of the \p releasedReference can access or
162-
/// free memory accessed by \p User.
163-
bool mayValueReleaseInterfereWithInstruction(SILInstruction *User,
164-
SILValue releasedReference);
165-
166156
/// Use the alias analysis to determine the memory behavior of Inst with
167157
/// respect to V.
168158
MemoryBehavior computeMemoryBehavior(SILInstruction *Inst, SILValue V);
@@ -206,6 +196,19 @@ class AliasAnalysis {
206196

207197
/// Returns true if \p Ptr may be released by the builtin \p BI.
208198
bool canBuiltinDecrementRefCount(BuiltinInst *BI, SILValue Ptr);
199+
200+
/// Returns true if the address(es of) `addr` can escape to `toInst`.
201+
MemoryBehavior getMemoryBehaviorOfInst(SILValue addr, SILInstruction *toInst);
202+
203+
/// Returns true if the object(s of) `obj` can escape to `toInst`.
204+
bool isObjectReleasedByInst(SILValue obj, SILInstruction *toInst);
205+
206+
/// Is the `addr` within all reachable objects/addresses, when start walking
207+
/// from `obj`?
208+
bool isAddrVisibleFromObject(SILValue addr, SILValue obj);
209+
210+
/// Returns true if `lhs` can reference the same field as `rhs`.
211+
bool canReferenceSameField(SILValue lhs, SILValue rhs);
209212
};
210213

211214

include/swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,11 @@ class EpilogueARCContext {
5757
enum EpilogueARCKind { Retain = 0, Release = 1 };
5858

5959
private:
60+
SILPassManager *PM;
61+
6062
/// Current post-order we are using.
6163
LazyFunctionInfo<PostOrderAnalysis, PostOrderFunctionInfo> PO;
6264

63-
/// Current alias analysis we are using.
64-
AliasAnalysis *AA;
65-
6665
/// Current rc-identity we are using.
6766
LazyFunctionInfo<RCIdentityAnalysis, RCIdentityFunctionInfo> RCFI;
6867

@@ -130,9 +129,9 @@ class EpilogueARCContext {
130129

131130
public:
132131
/// Constructor.
133-
EpilogueARCContext(SILFunction *F, PostOrderAnalysis *PO, AliasAnalysis *AA,
132+
EpilogueARCContext(SILFunction *F, PostOrderAnalysis *PO, SILPassManager *PM,
134133
RCIdentityAnalysis *RCIA)
135-
: PO(F, PO), AA(AA), RCFI(F, RCIA) {}
134+
: PM(PM), PO(F, PO), RCFI(F, RCIA) {}
136135

137136
/// Run the data flow to find the epilogue retains or releases.
138137
bool run(EpilogueARCKind NewKind, SILValue NewArg) {
@@ -174,12 +173,13 @@ class EpilogueARCContext {
174173
bool mayBlockEpilogueRetain(SILInstruction *II, SILValue Ptr) {
175174
// reference decrementing instruction prevents any retain to be identified as
176175
// epilogue retains.
177-
if (mayDecrementRefCount(II, Ptr, AA))
176+
auto *function = II->getFunction();
177+
if (mayDecrementRefCount(II, Ptr, PM->getAnalysis<AliasAnalysis>(function)))
178178
return true;
179179
// Handle self-recursion. A self-recursion can be considered a +1 on the
180180
// current argument.
181181
if (auto *AI = dyn_cast<ApplyInst>(II))
182-
if (AI->getCalleeFunction() == II->getParent()->getParent())
182+
if (AI->getCalleeFunction() == function)
183183
return true;
184184
return false;
185185
}
@@ -237,8 +237,8 @@ class EpilogueARCFunctionInfo {
237237
public:
238238
/// Constructor.
239239
EpilogueARCFunctionInfo(SILFunction *F, PostOrderAnalysis *PO,
240-
AliasAnalysis *AA, RCIdentityAnalysis *RC)
241-
: Context(F, PO, AA, RC) {}
240+
SILPassManager *PM, RCIdentityAnalysis *RC)
241+
: Context(F, PO, PM, RC) {}
242242

243243
/// Find the epilogue ARC instruction based on the given \p Kind and given
244244
/// \p Arg.

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,18 @@ SwiftInt PostDominatorTree_postDominates(BridgedPostDomTree pdomTree,
127127
BridgedBasicBlock dominating,
128128
BridgedBasicBlock dominated);
129129

130+
typedef BridgedMemoryBehavior (* _Nonnull AliasAnalysisGetMemEffectFn)(
131+
BridgedPassContext context, BridgedValue, BridgedInstruction);
132+
typedef bool (* _Nonnull AliasAnalysisEscaping2InstFn)(
133+
BridgedPassContext context, BridgedValue, BridgedInstruction);
134+
typedef bool (* _Nonnull AliasAnalysisEscaping2ValFn)(
135+
BridgedPassContext context, BridgedValue, BridgedValue);
136+
137+
void AliasAnalysis_register(AliasAnalysisGetMemEffectFn getMemEffectsFn,
138+
AliasAnalysisEscaping2InstFn isObjReleasedFn,
139+
AliasAnalysisEscaping2ValFn isAddrVisibleFromObjFn,
140+
AliasAnalysisEscaping2ValFn mayPointToSameAddrFn);
141+
130142
BridgedSlab PassContext_getNextSlab(BridgedSlab slab);
131143
BridgedSlab PassContext_getPreviousSlab(BridgedSlab slab);
132144
BridgedSlab PassContext_allocSlab(BridgedPassContext passContext,

lib/SILOptimizer/Analysis/ARCAnalysis.cpp

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -282,24 +282,14 @@ bool swift::mayHaveSymmetricInterference(SILInstruction *User, SILValue Ptr, Ali
282282
if (!canUseObject(User))
283283
return false;
284284

285-
// Check whether releasing this value can call deinit and interfere with User.
286-
if (AA->mayValueReleaseInterfereWithInstruction(User, Ptr))
287-
return true;
288-
289-
// If the user is a load or a store and we can prove that it does not access
290-
// the object then return true.
291-
// Notice that we need to check all of the values of the object.
292-
if (isa<StoreInst>(User)) {
293-
if (AA->mayWriteToMemory(User, Ptr))
294-
return true;
295-
return false;
285+
if (auto *LI = dyn_cast<LoadInst>(User)) {
286+
return AA->isAddrVisibleFromObject(LI->getOperand(), Ptr);
296287
}
297-
298-
if (isa<LoadInst>(User) ) {
299-
if (AA->mayReadFromMemory(User, Ptr))
300-
return true;
301-
return false;
288+
if (auto *SI = dyn_cast<StoreInst>(User)) {
289+
return AA->isAddrVisibleFromObject(SI->getDest(), Ptr);
302290
}
291+
if (User->mayReadOrWriteMemory())
292+
return true;
303293

304294
// If we have a terminator instruction, see if it can use ptr. This currently
305295
// means that we first show that TI cannot indirectly use Ptr and then use

0 commit comments

Comments
 (0)