Skip to content

Commit 66f07fe

Browse files
committed
Swift SIL: rework function effects and add function side-effects
So far, function effects only included escape effects. This change adds side-effects (but they are not computed, yet). It also involves refactoring of the existing escape effects. Also the SIL effect syntax changed a bit. Details are in docs/SIL.rst
1 parent 99079ec commit 66f07fe

File tree

18 files changed

+744
-296
lines changed

18 files changed

+744
-296
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeEffects.swift

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,43 +12,47 @@
1212

1313
import SIL
1414

15-
/// Computes effects for function arguments.
15+
/// Computes escape effects for function arguments.
1616
///
1717
/// For example, if an argument does not escape, adds a non-escaping effect,
18-
/// e.g. "[escapes !%0.**]":
19-
///
20-
/// sil [escapes !%0.**] @foo : $@convention(thin) (@guaranteed X) -> () {
18+
/// ```
19+
/// sil @foo : $@convention(thin) (@guaranteed X) -> () {
20+
/// [%0: noecape **]
2121
/// bb0(%0 : $X):
2222
/// %1 = tuple ()
2323
/// return %1 : $()
2424
/// }
25-
///
25+
/// ```
2626
/// The pass does not try to change or re-compute _defined_ effects.
27-
/// Currently, only escaping effects are handled.
28-
/// In future, this pass may also add other effects, like memory side effects.
27+
///
2928
let computeEffects = FunctionPass(name: "compute-effects", {
3029
(function: Function, context: PassContext) in
31-
var argsWithDefinedEffects = getArgIndicesWithDefinedEffects(of: function)
3230

33-
struct IgnoreRecursiveCallVisitor : EscapeVisitor {
34-
func visitUse(operand: Operand, path: EscapePath) -> UseResult {
35-
return isOperandOfRecursiveCall(operand) ? .ignore : .continueWalk
36-
}
37-
}
38-
var newEffects = Stack<ArgumentEffect>(context)
31+
var newEffects = Stack<EscapeEffects.ArgumentEffect>(context)
32+
defer { newEffects.deinitialize() }
33+
3934
let returnInst = function.returnInstruction
35+
let argsWithDefinedEffects = getArgIndicesWithDefinedEscapingEffects(of: function)
4036

4137
for arg in function.arguments {
4238
// We are not interested in arguments with trivial types.
4339
if !arg.type.isNonTrivialOrContainsRawPointer(in: function) { continue }
4440

4541
// Also, we don't want to override defined effects.
4642
if argsWithDefinedEffects.contains(arg.index) { continue }
47-
43+
44+
struct IgnoreRecursiveCallVisitor : EscapeVisitor {
45+
func visitUse(operand: Operand, path: EscapePath) -> UseResult {
46+
return isOperandOfRecursiveCall(operand) ? .ignore : .continueWalk
47+
}
48+
}
49+
4850
// First check: is the argument (or a projected value of it) escaping at all?
4951
if !arg.at(.anything).isEscapingWhenWalkingDown(using: IgnoreRecursiveCallVisitor(),
5052
context) {
51-
newEffects.push(ArgumentEffect(.notEscaping, argumentIndex: arg.index, pathPattern: SmallProjectionPath(.anything)))
53+
let effect = EscapeEffects.ArgumentEffect(.notEscaping, argumentIndex: arg.index,
54+
pathPattern: SmallProjectionPath(.anything))
55+
newEffects.push(effect)
5256
continue
5357
}
5458

@@ -62,17 +66,16 @@ let computeEffects = FunctionPass(name: "compute-effects", {
6266
}
6367

6468
context.modifyEffects(in: function) { (effects: inout FunctionEffects) in
65-
effects.removeDerivedEffects()
66-
effects.argumentEffects.append(contentsOf: newEffects)
69+
effects.escapeEffects.arguments = effects.escapeEffects.arguments.filter { !$0.isDerived }
70+
effects.escapeEffects.arguments.append(contentsOf: newEffects)
6771
}
68-
newEffects.removeAll()
6972
})
7073

7174

7275
/// Returns true if an argument effect was added.
7376
private
7477
func addArgEffects(_ arg: FunctionArgument, argPath ap: SmallProjectionPath,
75-
to newEffects: inout Stack<ArgumentEffect>,
78+
to newEffects: inout Stack<EscapeEffects.ArgumentEffect>,
7679
_ returnInst: ReturnInst?, _ context: PassContext) -> Bool {
7780
// Correct the path if the argument is not a class reference itself, but a value type
7881
// containing one or more references.
@@ -141,39 +144,36 @@ func addArgEffects(_ arg: FunctionArgument, argPath ap: SmallProjectionPath,
141144
return false
142145
}
143146

144-
let effect: ArgumentEffect
147+
let effect: EscapeEffects.ArgumentEffect
145148
switch result {
146149
case .notSet:
147-
effect = ArgumentEffect(.notEscaping, argumentIndex: arg.index, pathPattern: argPath)
150+
effect = EscapeEffects.ArgumentEffect(.notEscaping, argumentIndex: arg.index, pathPattern: argPath)
148151
case .toReturn(let toPath):
149152
let exclusive = isExclusiveEscapeToReturn(fromArgument: arg, fromPath: argPath,
150153
toPath: toPath, returnInst: returnInst, context)
151-
effect = ArgumentEffect(.escapingToReturn(toPath, exclusive),
152-
argumentIndex: arg.index, pathPattern: argPath)
154+
effect = EscapeEffects.ArgumentEffect(.escapingToReturn(toPath, exclusive),
155+
argumentIndex: arg.index, pathPattern: argPath)
153156
case .toArgument(let toArgIdx, let toPath):
154157
let exclusive = isExclusiveEscapeToArgument(fromArgument: arg, fromPath: argPath,
155158
toArgumentIndex: toArgIdx, toPath: toPath, context)
156-
effect = ArgumentEffect(.escapingToArgument(toArgIdx, toPath, exclusive),
157-
argumentIndex: arg.index, pathPattern: argPath)
159+
effect = EscapeEffects.ArgumentEffect(.escapingToArgument(toArgIdx, toPath, exclusive),
160+
argumentIndex: arg.index, pathPattern: argPath)
158161
}
159162
newEffects.push(effect)
160163
return true
161164
}
162165

163166
/// Returns a set of argument indices for which there are "defined" effects (as opposed to derived effects).
164-
private func getArgIndicesWithDefinedEffects(of function: Function) -> Set<Int> {
167+
private func getArgIndicesWithDefinedEscapingEffects(of function: Function) -> Set<Int> {
165168
var argsWithDefinedEffects = Set<Int>()
166169

167-
for effect in function.effects.argumentEffects {
170+
for effect in function.effects.escapeEffects.arguments {
168171
if effect.isDerived { continue }
169172

170173
argsWithDefinedEffects.insert(effect.argumentIndex)
171-
172174
switch effect.kind {
173-
case .notEscaping, .escapingToReturn:
174-
break
175-
case .escapingToArgument(let toArgIdx, _, _):
176-
argsWithDefinedEffects.insert(toArgIdx)
175+
case .notEscaping, .escapingToReturn: break
176+
case .escapingToArgument(let toArgIdx, _, _): argsWithDefinedEffects.insert(toArgIdx)
177177
}
178178
}
179179
return argsWithDefinedEffects

SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
562562
guard let destructor = calleeAnalysis.getDestructor(ofExactType: exactTy) else {
563563
return isEscaping
564564
}
565-
if destructor.effects.canEscape(argumentIndex: 0, path: p, analyzeAddresses: analyzeAddresses) {
565+
if destructor.effects.escapeEffects.canEscape(argumentIndex: 0, path: p, analyzeAddresses: analyzeAddresses) {
566566
return isEscaping
567567
}
568568
} else {
@@ -572,7 +572,7 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
572572
return isEscaping
573573
}
574574
for destructor in destructors {
575-
if destructor.effects.canEscape(argumentIndex: 0, path: p, analyzeAddresses: analyzeAddresses) {
575+
if destructor.effects.escapeEffects.canEscape(argumentIndex: 0, path: p, analyzeAddresses: analyzeAddresses) {
576576
return isEscaping
577577
}
578578
}
@@ -610,7 +610,7 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
610610

611611
for callee in callees {
612612
let effects = callee.effects
613-
if !effects.canEscape(argumentIndex: calleeArgIdx, path: path.projectionPath, analyzeAddresses: analyzeAddresses) {
613+
if !effects.escapeEffects.canEscape(argumentIndex: calleeArgIdx, path: path.projectionPath, analyzeAddresses: analyzeAddresses) {
614614
continue
615615
}
616616
if walkDownArgument(calleeArgIdx: calleeArgIdx, argPath: path,
@@ -626,7 +626,7 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
626626
func walkDownArgument(calleeArgIdx: Int, argPath: Path,
627627
apply: ApplySite, effects: FunctionEffects) -> WalkResult {
628628
var matched = false
629-
for effect in effects.argumentEffects {
629+
for effect in effects.escapeEffects.arguments {
630630
switch effect.kind {
631631
case .escapingToArgument(let toArgIdx, let toPath, let exclusive):
632632
if effect.matches(calleeArgIdx, argPath.projectionPath) {
@@ -670,7 +670,7 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
670670
}
671671
matched = true
672672
}
673-
default:
673+
case .notEscaping:
674674
break
675675
}
676676
}
@@ -787,10 +787,8 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
787787

788788
for callee in callees {
789789
var matched = false
790-
for effect in callee.effects.argumentEffects {
790+
for effect in callee.effects.escapeEffects.arguments {
791791
switch effect.kind {
792-
case .notEscaping, .escapingToArgument:
793-
break
794792
case .escapingToReturn(let toPath, let exclusive):
795793
if exclusive && path.projectionPath.matches(pattern: toPath) {
796794
let arg = apply.arguments[effect.argumentIndex]
@@ -801,6 +799,8 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
801799
}
802800
matched = true
803801
}
802+
case .notEscaping, .escapingToArgument:
803+
break
804804
}
805805
}
806806
if !matched {

0 commit comments

Comments
 (0)