Skip to content

Commit efa0646

Browse files
committed
Use the new EscapeInfo in AliasAnalysis
1 parent f6ad39e commit efa0646

22 files changed

+520
-381
lines changed

SwiftCompilerSources/Sources/Optimizer/Analysis/AliasAnalysis.swift

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,108 @@ struct AliasAnalysis {
6060
// Any other non-address value means: all addresses of any referenced class instances within the value.
6161
return EscapeInfo.Path(.anyValueFields).push(.anyClassField).push(.anyValueFields)
6262
}
63+
64+
static func register() {
65+
AliasAnalysis_register(
66+
// initFn
67+
{ (buffer: UnsafeMutableRawPointer, size: Int, ca: BridgedCalleeAnalysis) in
68+
precondition(MemoryLayout<EscapeInfo>.size <= size, "wrong EscapeInfo size")
69+
let ei = EscapeInfo(calleeAnalysis: CalleeAnalysis(bridged: ca))
70+
buffer.initializeMemory(as: EscapeInfo.self, repeating: ei, count: 1)
71+
},
72+
// destroyFn
73+
{ (buffer: UnsafeMutableRawPointer, size: Int) in
74+
precondition(MemoryLayout<EscapeInfo>.size <= size, "wrong EscapeInfo size")
75+
buffer.assumingMemoryBound(to: EscapeInfo.self).deinitialize(count: 1)
76+
},
77+
// isAddrEscaping2InstFn
78+
{ (buffer: UnsafeMutableRawPointer, bridgedVal: BridgedValue, bridgedInst: BridgedInstruction,
79+
readingOpsMask: UInt64, writingOpsMask: UInt64) -> BridgedMemoryBehavior in
80+
let eiPointer = buffer.assumingMemoryBound(to: EscapeInfo.self)
81+
let inst = bridgedInst.instruction
82+
let val = bridgedVal.value
83+
let path = AliasAnalysis.getPtrOrAddressPath(for: val)
84+
var isReading = false
85+
var isWriting = false
86+
if eiPointer.pointee.isEscaping(addressesOf: val, path: path,
87+
visitUse: { op, _, _ in
88+
let user = op.instruction
89+
if user == inst {
90+
let opIdx = op.index
91+
if opIdx >= 64 || (readingOpsMask & (UInt64(1) << opIdx)) != 0 {
92+
isReading = true
93+
}
94+
if opIdx >= 64 || (writingOpsMask & (UInt64(1) << opIdx)) != 0 {
95+
isWriting = true
96+
}
97+
}
98+
if user is ReturnInst {
99+
// Anything which is returned cannot escape to an instruction inside the function.
100+
return .ignore
101+
}
102+
return .continueWalking
103+
}) {
104+
return MayReadWriteBehavior
105+
}
106+
switch (isReading, isWriting) {
107+
case (false, false): return NoneBehavior
108+
case (true, false): return MayReadBehavior
109+
case (false, true): return MayWriteBehavior
110+
case (true, true): return MayReadWriteBehavior
111+
}
112+
},
113+
// isObjEscaping2InstFn
114+
{ (buffer: UnsafeMutableRawPointer, bridgedObj: BridgedValue, bridgedInst: BridgedInstruction,
115+
operandMask: UInt64) -> Int in
116+
let eiPointer = buffer.assumingMemoryBound(to: EscapeInfo.self)
117+
let inst = bridgedInst.instruction
118+
let obj = bridgedObj.value
119+
let path = EscapeInfo.Path(.anyValueFields)
120+
return eiPointer.pointee.isEscaping(object: obj, path: path,
121+
visitUse: { op, _, _ in
122+
let user = op.instruction
123+
if user == inst {
124+
let opIdx = op.index
125+
if opIdx >= 64 || (operandMask & (UInt64(1) << opIdx)) != 0 {
126+
return .markEscaping
127+
}
128+
}
129+
if user is ReturnInst {
130+
// Anything which is returned cannot escape to an instruction inside the function.
131+
return .ignore
132+
}
133+
return .continueWalking
134+
}) ? 1 : 0
135+
},
136+
// isAddrVisibleFromObj
137+
{ (buffer: UnsafeMutableRawPointer, bridgedAddr: BridgedValue, bridgedObj: BridgedValue) -> Int in
138+
let eiPointer = buffer.assumingMemoryBound(to: EscapeInfo.self)
139+
let addr = bridgedAddr.value
140+
let obj = bridgedObj.value
141+
142+
// Is the `addr` within all reachable objects/addresses, when start walking from `obj`?
143+
// Try both directions: 1. from addr -> obj
144+
return eiPointer.pointee.isEscaping(addressesOf: addr, path: EscapeInfo.Path(.anyValueFields),
145+
visitUse: { op, _, _ in
146+
if op.value == obj {
147+
return .markEscaping
148+
}
149+
if op.instruction is ReturnInst {
150+
// Anything which is returned cannot escape to an instruction inside the function.
151+
return .ignore
152+
}
153+
return .continueWalking
154+
}) ? 1 : 0
155+
},
156+
// canReferenceSameFieldFn
157+
{ (buffer: UnsafeMutableRawPointer, bridgedLhs: BridgedValue, bridgedRhs: BridgedValue) -> Int in
158+
let eiPointer = buffer.assumingMemoryBound(to: EscapeInfo.self)
159+
let lhs = bridgedLhs.value
160+
let rhs = bridgedRhs.value
161+
return eiPointer.pointee.canReferenceSameField(
162+
lhs, path: AliasAnalysis.getPtrOrAddressPath(for: lhs),
163+
rhs, path: AliasAnalysis.getPtrOrAddressPath(for: rhs)) ? 1 : 0
164+
}
165+
)
166+
}
63167
}

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import _CompilerRegexParser
2020
@_cdecl("initializeSwiftModules")
2121
public func initializeSwiftModules() {
2222
registerSILClasses()
23+
registerAnalysis()
2324
registerSwiftPasses()
2425

2526
#if canImport(_CompilerRegexParser)
@@ -43,6 +44,10 @@ private func registerPass<InstType: Instruction>(
4344
}
4445
}
4546

47+
private func registerAnalysis() {
48+
AliasAnalysis.register()
49+
}
50+
4651
private func registerSwiftPasses() {
4752
registerPass(silPrinterPass, { silPrinterPass.run($0) })
4853
registerPass(mergeCondFailsPass, { mergeCondFailsPass.run($0) })

SwiftCompilerSources/Sources/SIL/Operand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import SILBridging
1616
public struct Operand : CustomStringConvertible, CustomReflectable {
1717
fileprivate let bridged: BridgedOperand
1818

19-
init(_ bridged: BridgedOperand) {
19+
public init(_ bridged: BridgedOperand) {
2020
self.bridged = bridged
2121
}
2222

include/swift/SILOptimizer/Analysis/AliasAnalysis.h

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
namespace swift {
2222

2323
class SideEffectAnalysis;
24-
class EscapeAnalysis;
2524

2625
/// This class is a simple wrapper around an alias analysis cache. This is
2726
/// needed since we do not have an "analysis" infrastructure.
@@ -75,7 +74,8 @@ class AliasAnalysis {
7574
using TBAACacheKey = std::pair<SILType, SILType>;
7675

7776
SideEffectAnalysis *SEA;
78-
EscapeAnalysis *EA;
77+
78+
void *escapeInfoBuffer[6];
7979

8080
/// A cache for the computation of TBAA. True means that the types may
8181
/// alias. False means that the types must not alias.
@@ -125,8 +125,9 @@ class AliasAnalysis {
125125
bool isInImmutableScope(SILInstruction *inst, SILValue V);
126126

127127
public:
128-
AliasAnalysis(SideEffectAnalysis *SEA, EscapeAnalysis *EA)
129-
: SEA(SEA), EA(EA) {}
128+
AliasAnalysis(SideEffectAnalysis *SEA);
129+
130+
~AliasAnalysis();
130131

131132
static SILAnalysisKind getAnalysisKind() { return SILAnalysisKind::Alias; }
132133

@@ -158,11 +159,6 @@ class AliasAnalysis {
158159
return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::MayAlias;
159160
}
160161

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-
166162
/// Use the alias analysis to determine the memory behavior of Inst with
167163
/// respect to V.
168164
MemoryBehavior computeMemoryBehavior(SILInstruction *Inst, SILValue V);
@@ -206,6 +202,22 @@ class AliasAnalysis {
206202

207203
/// Returns true if \p Ptr may be released by the builtin \p BI.
208204
bool canBuiltinDecrementRefCount(BuiltinInst *BI, SILValue Ptr);
205+
206+
/// Returns true if the address(es of) `addr` can escape to `toInst`.
207+
MemoryBehavior getAddressEscapingBehavior(SILValue addr, SILInstruction *toInst,
208+
uint64_t readingOpsMask = ~uint64_t(0),
209+
uint64_t writingOpsMask = ~uint64_t(0));
210+
211+
/// Returns true if the object(s of) `obj` can escape to `toInst`.
212+
bool isObjectEscaping(SILValue obj, SILInstruction *toInst,
213+
uint64_t operandMask = ~uint64_t(0));
214+
215+
/// Is the `addr` within all reachable objects/addresses, when start walking
216+
/// from `obj`?
217+
bool isAddrVisibleFromObject(SILValue addr, SILValue obj);
218+
219+
/// Returns true if `lhs` can reference the same field as `rhs`.
220+
bool canReferenceSameField(SILValue lhs, SILValue rhs);
209221
};
210222

211223

include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ class GenericFunctionEffectAnalysis : public BottomUpIPAnalysis {
113113
getCalleeEffects(calleeEffects, fullApply);
114114
callEffects.mergeFromApply(calleeEffects, fullApply);
115115
}
116+
117+
BasicCalleeAnalysis *getBasicCalleeAnalysis() { return BCA; }
116118

117119
virtual void initialize(SILPassManager *PM) override;
118120

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
#include "swift/SIL/SILBridging.h"
1717

18+
#include <stdint.h>
19+
1820
SWIFT_BEGIN_NULLABILITY_ANNOTATIONS
1921

2022
#ifdef __cplusplus
@@ -121,6 +123,23 @@ SwiftInt PostDominatorTree_postDominates(BridgedPostDomTree pdomTree,
121123
BridgedBasicBlock dominating,
122124
BridgedBasicBlock dominated);
123125

126+
typedef void (* _Nonnull AliasAnalysisInitFn)(void * _Nonnull, SwiftInt,
127+
BridgedCalleeAnalysis);
128+
typedef void (* _Nonnull AliasAnalysisDestroyFn)(void * _Nonnull, SwiftInt);
129+
typedef BridgedMemoryBehavior (* _Nonnull AliasAnalysisAddr2InstFn)(
130+
void * _Nonnull, BridgedValue, BridgedInstruction, uint64_t, uint64_t);
131+
typedef SwiftInt (* _Nonnull AliasAnalysisEscaping2InstFn)(
132+
void * _Nonnull, BridgedValue, BridgedInstruction, uint64_t);
133+
typedef SwiftInt (* _Nonnull AliasAnalysisEscaping2ValFn)(
134+
void * _Nonnull, BridgedValue, BridgedValue);
135+
136+
void AliasAnalysis_register(AliasAnalysisInitFn initFn,
137+
AliasAnalysisDestroyFn destroyFn,
138+
AliasAnalysisAddr2InstFn isAddrEscaping2InstFn,
139+
AliasAnalysisEscaping2InstFn isObjEscaping2InstFn,
140+
AliasAnalysisEscaping2ValFn isAddrVisibleFromObjFn,
141+
AliasAnalysisEscaping2ValFn mayPointToSameAddrFn);
142+
124143
BridgedSlab PassContext_getNextSlab(BridgedSlab slab);
125144
BridgedSlab PassContext_getPreviousSlab(BridgedSlab slab);
126145
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)