Skip to content

Commit 4d5987c

Browse files
committed
add passes to dump the results of EscapeInfo
And add test files which uses the passes for verification of EscapeInfo
1 parent 9dc3bd6 commit 4d5987c

File tree

8 files changed

+1854
-0
lines changed

8 files changed

+1854
-0
lines changed

SwiftCompilerSources/Sources/Optimizer/Analysis/AliasAnalysis.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,21 @@ struct AliasAnalysis {
4343
return false
4444
}
4545
}
46+
47+
/// Returns the correct path for address-alias functions.
48+
static func getPtrOrAddressPath(for value: Value) -> EscapeInfo.Path {
49+
let ty = value.type
50+
if ty.isAddress {
51+
// This is the regular case: the path selects any sub-fields of an address.
52+
return EscapeInfo.Path(.anyValueFields)
53+
}
54+
// Some optimizations use the address-alias APIs with non-address SIL values.
55+
// TODO: this is non-intuitive and we should eliminate those API uses.
56+
if ty.isClass {
57+
// If the value is a (non-address) reference it means: all addresses within the class instance.
58+
return EscapeInfo.Path(.anyValueFields).push(.anyClassField)
59+
}
60+
// Any other non-address value means: all addresses of any referenced class instances within the value.
61+
return EscapeInfo.Path(.anyValueFields).push(.anyClassField).push(.anyValueFields)
62+
}
4663
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
swift_compiler_sources(Optimizer
1010
AssumeSingleThreaded.swift
11+
EscapeInfoDumper.swift
1112
SILPrinter.swift
1213
MergeCondFails.swift
1314
RangeDumper.swift
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
//===--- EscapeInfoDumper.swift - Dumps escape information ----------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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+
/// Dumps the results of escape analysis.
16+
///
17+
/// Dumps the EscapeInfo query results for all `alloc_stack` instructions in a function.
18+
///
19+
/// This pass is used for testing EscapeInfo.
20+
let escapeInfoDumper = FunctionPass(name: "dump-escape-info", {
21+
(function: Function, context: PassContext) in
22+
23+
print("Escape information for \(function.name):")
24+
25+
var escapeInfo = EscapeInfo(calleeAnalysis: context.calleeAnalysis)
26+
27+
for block in function.blocks {
28+
for inst in block.instructions {
29+
if let allocRef = inst as? AllocRefInst {
30+
var results = Set<String>()
31+
32+
let escapes = escapeInfo.isEscaping(object: allocRef,
33+
visitUse: { op, path, _ in
34+
if op.instruction is ReturnInst {
35+
results.insert("return[\(path)]")
36+
return .ignore
37+
}
38+
return .continueWalking
39+
},
40+
visitDef: { def, path, followStores in
41+
guard let arg = def as? FunctionArgument else {
42+
return .continueWalkingUp
43+
}
44+
results.insert("arg\(arg.index)[\(path)]")
45+
return .continueWalkingDown
46+
})
47+
48+
let res: String
49+
if escapes {
50+
res = "global"
51+
} else if results.isEmpty {
52+
res = " - "
53+
} else {
54+
res = Array(results).sorted().joined(separator: ",")
55+
}
56+
print("\(res): \(allocRef)")
57+
}
58+
}
59+
}
60+
print("End function \(function.name)\n")
61+
})
62+
63+
/// Dumps the results of address-related escape analysis.
64+
///
65+
/// Dumps the EscapeInfo query results for addresses escaping to function calls.
66+
/// The `fix_lifetime` instruction is used as marker for addresses and values to query.
67+
///
68+
/// This pass is used for testing EscapeInfo.
69+
let addressEscapeInfoDumper = FunctionPass(name: "dump-addr-escape-info", {
70+
(function: Function, context: PassContext) in
71+
72+
print("Address escape information for \(function.name):")
73+
74+
var valuesToCheck = [Value]()
75+
var applies = [Instruction]()
76+
77+
for block in function.blocks {
78+
for inst in block.instructions {
79+
switch inst {
80+
case let fli as FixLifetimeInst:
81+
valuesToCheck.append(fli.operand)
82+
case is FullApplySite:
83+
applies.append(inst)
84+
default:
85+
break
86+
}
87+
}
88+
}
89+
90+
var escapeInfo = EscapeInfo(calleeAnalysis: context.calleeAnalysis)
91+
92+
// test `isEscaping(addressesOf:)`
93+
for value in valuesToCheck {
94+
print("value:\(value)")
95+
for apply in applies {
96+
let path = AliasAnalysis.getPtrOrAddressPath(for: value)
97+
let escaping = escapeInfo.isEscaping(addressesOf: value, path: path,
98+
visitUse: { op, _, _ in
99+
let user = op.instruction
100+
if user == apply {
101+
return .markEscaping
102+
}
103+
if user is ReturnInst {
104+
// Anything which is returned cannot escape to an instruction inside the function.
105+
return .ignore
106+
}
107+
return .continueWalking
108+
})
109+
print(" \(escaping ? "==>" : "- ") \(apply)")
110+
}
111+
}
112+
113+
// test `canReferenceSameField` for each pair of `fix_lifetime`.
114+
if !valuesToCheck.isEmpty {
115+
for lhsIdx in 0..<(valuesToCheck.count - 1) {
116+
for rhsIdx in (lhsIdx + 1) ..< valuesToCheck.count {
117+
print("pair \(lhsIdx) - \(rhsIdx)")
118+
let lhs = valuesToCheck[lhsIdx]
119+
let rhs = valuesToCheck[rhsIdx]
120+
print(lhs)
121+
print(rhs)
122+
if escapeInfo.canReferenceSameField(
123+
lhs, path: AliasAnalysis.getPtrOrAddressPath(for: lhs),
124+
rhs, path: AliasAnalysis.getPtrOrAddressPath(for: rhs)) {
125+
print("may alias")
126+
} else {
127+
print("no alias")
128+
}
129+
}
130+
}
131+
}
132+
133+
print("End function \(function.name)\n")
134+
})

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ private func registerPass<InstType: Instruction>(
4646
private func registerSwiftPasses() {
4747
registerPass(silPrinterPass, { silPrinterPass.run($0) })
4848
registerPass(mergeCondFailsPass, { mergeCondFailsPass.run($0) })
49+
registerPass(escapeInfoDumper, { escapeInfoDumper.run($0) })
50+
registerPass(addressEscapeInfoDumper, { addressEscapeInfoDumper.run($0) })
4951
registerPass(simplifyBeginCOWMutationPass, { simplifyBeginCOWMutationPass.run($0) })
5052
registerPass(simplifyGlobalValuePass, { simplifyGlobalValuePass.run($0) })
5153
registerPass(simplifyStrongRetainPass, { simplifyStrongRetainPass.run($0) })

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ PASS(EmitDFDiagnostics, "dataflow-diagnostics",
224224
"Emit SIL Diagnostics")
225225
PASS(EscapeAnalysisDumper, "escapes-dump",
226226
"Dump Escape Analysis Results")
227+
SWIFT_FUNCTION_PASS(EscapeInfoDumper, "dump-escape-info",
228+
"Dumps escape information")
229+
SWIFT_FUNCTION_PASS(AddressEscapeInfoDumper, "dump-addr-escape-info",
230+
"Dumps address escape information")
227231
PASS(FlowIsolation, "flow-isolation",
228232
"Enforces flow-sensitive actor isolation rules")
229233
PASS(FunctionOrderPrinter, "function-order-printer",

0 commit comments

Comments
 (0)