Skip to content

Commit f9b524b

Browse files
committed
AliasAnalysis: a complete overhaul of alias- and memory-behavior analysis
The main changes are: *) Rewrite everything in swift. So far, parts of memory-behavior analysis were already implemented in swift. Now everything is done in swift and lives in `AliasAnalysis.swift`. This is a big code simplification. *) Support many more instructions in the memory-behavior analysis - especially OSSA instructions, like `begin_borrow`, `end_borrow`, `store_borrow`, `load_borrow`. The computation of end_borrow effects is now much more precise. Also, partial_apply is now handled more precisely. *) Simplify and reduce type-based alias analysis (TBAA). The complexity of the old TBAA comes from old days where the language and SIL didn't have strict aliasing and exclusivity rules (e.g. for inout arguments). Now TBAA is only needed for code using unsafe pointers. The new TBAA handles this - and not more. Note that TBAA for classes is already done in `AccessBase.isDistinct`. *) Handle aliasing in `begin_access [modify]` scopes. We already supported truly immutable scopes like `begin_access [read]` or `ref_element_addr [immutable]`. For `begin_access [modify]` we know that there are no other reads or writes to the access-address within the scope. *) Don't cache memory-behavior results. It turned out that the hit-miss rate was pretty bad (~ 1:7). The overhead of the cache lookup took as long as recomputing the memory behavior.
1 parent ceda41c commit f9b524b

34 files changed

+1735
-2494
lines changed

SwiftCompilerSources/Sources/Optimizer/Analysis/AliasAnalysis.swift

Lines changed: 773 additions & 129 deletions
Large diffs are not rendered by default.

SwiftCompilerSources/Sources/Optimizer/Analysis/CalleeAnalysis.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public struct CalleeAnalysis {
2424
},
2525
// getMemBehaviorFn
2626
{ (bridgedApply: BridgedInstruction, observeRetains: Bool, bca: BridgedCalleeAnalysis) -> BridgedMemoryBehavior in
27-
let apply = bridgedApply.instruction as! ApplySite
27+
let apply = bridgedApply.instruction as! FullApplySite
2828
let e = bca.analysis.getSideEffects(ofApply: apply)
2929
return e.getMemBehavior(observeRetains: observeRetains)
3030
}
@@ -60,7 +60,7 @@ public struct CalleeAnalysis {
6060
}
6161

6262
/// Returns the global (i.e. not argument specific) side effects of an apply.
63-
public func getSideEffects(ofApply apply: ApplySite) -> SideEffects.GlobalEffects {
63+
public func getSideEffects(ofApply apply: FullApplySite) -> SideEffects.GlobalEffects {
6464
return getSideEffects(ofCallee: apply.callee)
6565
}
6666

@@ -78,7 +78,7 @@ public struct CalleeAnalysis {
7878
}
7979

8080
/// Returns the argument specific side effects of an apply.
81-
public func getSideEffects(of apply: ApplySite, operand: Operand, path: SmallProjectionPath) -> SideEffects.GlobalEffects {
81+
public func getSideEffects(of apply: FullApplySite, operand: Operand, path: SmallProjectionPath) -> SideEffects.GlobalEffects {
8282
var result = SideEffects.GlobalEffects()
8383
guard let calleeArgIdx = apply.calleeArgumentIndex(of: operand) else {
8484
return result

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,11 +258,6 @@ struct FunctionPassContext : MutatingContext {
258258
SimplifyContext(_bridged: _bridged, notifyInstructionChanged: notifyInstructionChanged, preserveDebugInfo: preserveDebugInfo)
259259
}
260260

261-
var aliasAnalysis: AliasAnalysis {
262-
let bridgedAA = _bridged.getAliasAnalysis()
263-
return AliasAnalysis(bridged: bridgedAA)
264-
}
265-
266261
var deadEndBlocks: DeadEndBlocksAnalysis {
267262
let bridgeDEA = _bridged.getDeadEndBlocksAnalysis()
268263
return DeadEndBlocksAnalysis(bridged: bridgeDEA)

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ private func registerSwiftPasses() {
110110
registerForSILCombine(DestructureTupleInst.self, { run(DestructureTupleInst.self, $0) })
111111

112112
// Test passes
113+
registerPass(aliasInfoDumper, { aliasInfoDumper.run($0) })
113114
registerPass(functionUsesDumper, { functionUsesDumper.run($0) })
114115
registerPass(silPrinterPass, { silPrinterPass.run($0) })
115116
registerPass(escapeInfoDumper, { escapeInfoDumper.run($0) })
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//===--- AliasInfoDumper.swift --------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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+
import SIL
14+
15+
/// Prints the memory behavior of relevant instructions in relation to address values of the function.
16+
let aliasInfoDumper = FunctionPass(name: "dump-alias-info") {
17+
(function: Function, context: FunctionPassContext) in
18+
19+
let aliasAnalysis = context.aliasAnalysis
20+
21+
print("@\(function.name)")
22+
23+
let values = function.allValues
24+
25+
var pair = 0
26+
for (index1, value1) in values.enumerated() {
27+
for (index2, value2) in values.enumerated() {
28+
if index2 >= index1 {
29+
let result = aliasAnalysis.mayAlias(value1, value2)
30+
precondition(result == aliasAnalysis.mayAlias(value2, value1), "alias analysis not symmetric")
31+
32+
print("PAIR #\(pair).")
33+
print(" \(value1)")
34+
print(" \(value2)")
35+
if result {
36+
print(" MayAlias")
37+
} else if !value1.uses.isEmpty && !value2.uses.isEmpty {
38+
print(" NoAlias")
39+
} else {
40+
print(" noalias?")
41+
}
42+
43+
pair += 1
44+
}
45+
}
46+
}
47+
}
48+
49+
private extension Function {
50+
var allValues: [Value] {
51+
var values: [Value] = []
52+
for block in blocks {
53+
values.append(contentsOf: block.arguments.map { $0 })
54+
for inst in block.instructions {
55+
values.append(contentsOf: inst.results)
56+
}
57+
}
58+
return values
59+
}
60+
}

SwiftCompilerSources/Sources/Optimizer/TestPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
swift_compiler_sources(Optimizer
1010
FunctionUsesDumper.swift
1111
AccessDumper.swift
12+
AliasInfoDumper.swift
1213
DeadEndBlockDumper.swift
1314
EscapeInfoDumper.swift
1415
MemBehaviorDumper.swift

SwiftCompilerSources/Sources/Optimizer/TestPasses/EscapeInfoDumper.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,7 @@ let addressEscapeInfoDumper = FunctionPass(name: "dump-addr-escape-info") {
109109
for value in valuesToCheck {
110110
print("value:\(value)")
111111
for apply in applies {
112-
let path = AliasAnalysis.getPtrOrAddressPath(for: value)
113-
114-
if value.at(path).isEscaping(using: Visitor(apply: apply), context) {
112+
if value.allContainedAddresss.isEscaping(using: Visitor(apply: apply), context) {
115113
print(" ==> \(apply)")
116114
} else {
117115
print(" - \(apply)")
@@ -129,8 +127,8 @@ let addressEscapeInfoDumper = FunctionPass(name: "dump-addr-escape-info") {
129127
print(lhs)
130128
print(rhs)
131129

132-
let projLhs = lhs.at(AliasAnalysis.getPtrOrAddressPath(for: lhs))
133-
let projRhs = rhs.at(AliasAnalysis.getPtrOrAddressPath(for: rhs))
130+
let projLhs = lhs.allContainedAddresss
131+
let projRhs = rhs.allContainedAddresss
134132
let mayAlias = projLhs.canAddressAlias(with: projRhs, context)
135133
if mayAlias != projRhs.canAddressAlias(with: projLhs, context) {
136134
fatalError("canAddressAlias(with:) must be symmetric")

SwiftCompilerSources/Sources/Optimizer/TestPasses/MemBehaviorDumper.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ let memBehaviorDumper = FunctionPass(name: "dump-mem-behavior") {
2727

2828
for value in values where value.definingInstruction != inst {
2929

30-
if value.type.isAddress || value is AddressToPointerInst {
30+
if value.type.isAddress {
3131
let read = inst.mayRead(fromAddress: value, aliasAnalysis)
3232
let write = inst.mayWrite(toAddress: value, aliasAnalysis)
3333
print("PAIR #\(currentPair).")
@@ -57,21 +57,22 @@ private extension Function {
5757
private extension Instruction {
5858
var shouldTest: Bool {
5959
switch self {
60-
case is ApplyInst,
61-
is TryApplyInst,
60+
case is ApplySite,
6261
is EndApplyInst,
63-
is BeginApplyInst,
6462
is AbortApplyInst,
6563
is BeginAccessInst,
6664
is EndAccessInst,
6765
is EndCOWMutationInst,
6866
is CopyValueInst,
6967
is DestroyValueInst,
68+
is IsUniqueInst,
7069
is EndBorrowInst,
7170
is LoadInst,
71+
is LoadBorrowInst,
7272
is StoreInst,
7373
is CopyAddrInst,
7474
is BuiltinInst,
75+
is StoreBorrowInst,
7576
is DebugValueInst:
7677
return true
7778
default:

SwiftCompilerSources/Sources/SIL/Effects.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,10 @@ public struct SideEffects : CustomStringConvertible, NoReflectionChildren {
604604
write = write || other.write
605605
}
606606

607+
public static var noEffects: Memory {
608+
Memory(read: false, write: false)
609+
}
610+
607611
public static var worstEffects: Memory {
608612
Memory(read: true, write: true)
609613
}

SwiftCompilerSources/Sources/SIL/Value.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,34 @@ extension Value {
202202
public func at(_ kind: SmallProjectionPath.FieldKind, index: Int = 0) -> ProjectedValue {
203203
ProjectedValue(value: self, path: SmallProjectionPath(kind, index: index))
204204
}
205+
206+
/// Projects all "contained" addresses of this value.
207+
///
208+
/// If this value is an address, projects all sub-fields of the address, e.g. struct fields.
209+
///
210+
/// If this value is not an address, projects all "interior" pointers of the value:
211+
/// If this value is a class, "interior" pointer means: an address of any stored property of the class instance.
212+
/// If this value is a struct or another value type, "interior" pointers refer to any stored propery addresses of
213+
/// any class references in the struct or value type. For example:
214+
///
215+
/// class C { var x: Int; var y: Int }
216+
/// struct S { var c1: C; var c2: C }
217+
/// let s: S
218+
///
219+
/// `s.allContainedAddresss` refers to `s.c1.x`, `s.c1.y`, `s.c2.x` and `s.c2.y`
220+
///
221+
public var allContainedAddresss: ProjectedValue {
222+
if type.isAddress {
223+
// This is the regular case: the path selects any sub-fields of an address.
224+
return at(SmallProjectionPath(.anyValueFields))
225+
}
226+
if type.isClass {
227+
// If the value is a (non-address) reference it means: all addresses within the class instance.
228+
return at(SmallProjectionPath(.anyValueFields).push(.anyClassField))
229+
}
230+
// Any other non-address value means: all addresses of any referenced class instances within the value.
231+
return at(SmallProjectionPath(.anyValueFields).push(.anyClassField).push(.anyValueFields))
232+
}
205233
}
206234

207235

0 commit comments

Comments
 (0)