@@ -31,10 +31,14 @@ fileprivate typealias Path = ArgumentEffect.Path
3131/// In future, this pass may also add other effects, like memory side effects.
3232let computeEffects = FunctionPass ( name: " compute-effects " , {
3333 ( function: Function , context: PassContext ) in
34-
3534 var argsWithDefinedEffects = getArgIndicesWithDefinedEffects ( of: function)
3635
37- var escapeInfo = EscapeInfo ( calleeAnalysis: context. calleeAnalysis)
36+ struct IgnoreRecursiveCallVisitor : EscapeInfoVisitor {
37+ func visitUse( operand: Operand , path: Path , state: State ) -> UseResult {
38+ return isOperandOfRecursiveCall ( operand) ? . ignore : . continueWalk
39+ }
40+ }
41+ var escapeInfo = EscapeInfo ( calleeAnalysis: context. calleeAnalysis, visitor: IgnoreRecursiveCallVisitor ( ) )
3842 var newEffects = Stack < ArgumentEffect > ( context)
3943 let returnInst = function. returnInstruction
4044
@@ -46,21 +50,18 @@ let computeEffects = FunctionPass(name: "compute-effects", {
4650 if argsWithDefinedEffects. contains ( arg. index) { continue }
4751
4852 // First check: is the argument (or a projected value of it) escaping at all?
49- if !escapeInfo. isEscapingWhenWalkingDown ( object: arg, path: Path ( . anything) ,
50- visitUse: { op, _, _ in
51- isOperandOfRecursiveCall ( op) ? . ignore : . continueWalking
52- } ) {
53+ if !escapeInfo. isEscapingWhenWalkingDown ( object: arg, path: Path ( . anything) ) {
5354 let selectedArg = Selection ( arg, pathPattern: Path ( . anything) )
5455 newEffects. push ( ArgumentEffect ( . notEscaping, selectedArg: selectedArg) )
5556 continue
5657 }
5758
5859 // Now compute effects for two important cases:
5960 // * the argument itself + any value projections, and...
60- if addArgEffects ( arg, argPath: Path ( ) , to: & newEffects, returnInst, & escapeInfo ) {
61+ if addArgEffects ( context : context , arg, argPath: Path ( ) , to: & newEffects, returnInst) {
6162 // * single class indirections
62- _ = addArgEffects ( arg, argPath: Path ( . anyValueFields) . push ( . anyClassField) ,
63- to: & newEffects, returnInst, & escapeInfo )
63+ _ = addArgEffects ( context : context , arg, argPath: Path ( . anyValueFields) . push ( . anyClassField) ,
64+ to: & newEffects, returnInst)
6465 }
6566 }
6667
@@ -71,60 +72,72 @@ let computeEffects = FunctionPass(name: "compute-effects", {
7172 newEffects. removeAll ( )
7273} )
7374
75+
7476/// Returns true if an argument effect was added.
7577private
76- func addArgEffects( _ arg: FunctionArgument , argPath ap: Path ,
78+ func addArgEffects( context : PassContext , _ arg: FunctionArgument , argPath ap: Path ,
7779 to newEffects: inout Stack < ArgumentEffect > ,
78- _ returnInst: ReturnInst ? ,
79- _ escapeInfo: inout EscapeInfo ) -> Bool {
80-
81- var toSelection : Selection ?
80+ _ returnInst: ReturnInst ? ) -> Bool {
8281 // Correct the path if the argument is not a class reference itself, but a value type
8382 // containing one or more references.
8483 let argPath = arg. type. isClass ? ap : ap. push ( . anyValueFields)
85-
86- if escapeInfo. isEscapingWhenWalkingDown ( object: arg, path: argPath,
87- visitUse: { op, path, followStores in
88- if op. instruction == returnInst {
89- // The argument escapes to the function return
90- if followStores {
91- // The escaping path must not introduce a followStores.
92- return . markEscaping
93- }
94- if let ta = toSelection {
95- if ta. value != . returnValue { return . markEscaping }
96- toSelection = Selection ( . returnValue, pathPattern: path. merge ( with: ta. pathPattern) )
97- } else {
98- toSelection = Selection ( . returnValue, pathPattern: path)
99- }
100- return . ignore
101- }
102- if isOperandOfRecursiveCall ( op) {
103- return . ignore
104- }
105- return . continueWalking
106- } ,
107- visitDef: { def, path, followStores in
108- guard let destArg = def as? FunctionArgument else {
109- return . continueWalkingUp
110- }
111- // The argument escapes to another argument (e.g. an out or inout argument)
112- if followStores {
84+
85+ struct ArgEffectsVisitor : EscapeInfoVisitor {
86+ init ( toSelection: Selection ? , returnInst: ReturnInst ? ) {
87+ self . toSelection = toSelection
88+ self . returnInst = returnInst
89+ }
90+
91+ var toSelection : Selection ?
92+ var returnInst : ReturnInst ?
93+
94+ mutating func visitUse( operand: Operand , path: Path , state: State ) -> UseResult {
95+ if operand. instruction == returnInst {
96+ // The argument escapes to the function return
97+ if state. followStores {
11398 // The escaping path must not introduce a followStores.
114- return . markEscaping
99+ return . abort
115100 }
116- let argIdx = destArg. index
117101 if let ta = toSelection {
118- if ta. value != . argument ( argIdx ) { return . markEscaping }
119- toSelection = Selection ( . argument ( argIdx ) , pathPattern: path. merge ( with: ta. pathPattern) )
102+ if ta. value != . returnValue { return . abort }
103+ toSelection = Selection ( . returnValue , pathPattern: path. merge ( with: ta. pathPattern) )
120104 } else {
121- toSelection = Selection ( . argument ( argIdx ) , pathPattern: path)
105+ toSelection = Selection ( . returnValue , pathPattern: path)
122106 }
123- return . continueWalkingDown
124- } ) {
107+ return . ignore
108+ }
109+ if isOperandOfRecursiveCall ( operand) {
110+ return . ignore
111+ }
112+ return . continueWalk
113+ }
114+
115+ mutating func visitDef( def: Value , path: Path , state: State ) -> DefResult {
116+ guard let destArg = def as? FunctionArgument else {
117+ return . continueWalkUp
118+ }
119+ // The argument escapes to another argument (e.g. an out or inout argument)
120+ if state. followStores {
121+ // The escaping path must not introduce a followStores.
122+ return . abort
123+ }
124+ let argIdx = destArg. index
125+ if let ta = toSelection {
126+ if ta. value != . argument( argIdx) { return . abort }
127+ toSelection = Selection ( . argument( argIdx) , pathPattern: path. merge ( with: ta. pathPattern) )
128+ } else {
129+ toSelection = Selection ( . argument( argIdx) , pathPattern: path)
130+ }
131+ return . walkDown
132+ }
133+ }
134+
135+ var walker = EscapeInfo ( calleeAnalysis: context. calleeAnalysis, visitor: ArgEffectsVisitor ( toSelection: nil , returnInst: returnInst) )
136+ if walker. isEscapingWhenWalkingDown ( object: arg, path: argPath) {
125137 return false
126138 }
127-
139+
140+ let toSelection = walker. visitor. toSelection
128141 let fromSelection = Selection ( arg, pathPattern: argPath)
129142
130143 guard let toSelection = toSelection else {
@@ -137,7 +150,7 @@ func addArgEffects(_ arg: FunctionArgument, argPath ap: Path,
137150 return false
138151 }
139152
140- let exclusive = isExclusiveEscape ( fromArgument: arg, fromPath: argPath, to: toSelection, returnInst, & escapeInfo )
153+ let exclusive = isExclusiveEscape ( context : context , fromArgument: arg, fromPath: argPath, to: toSelection, returnInst)
141154
142155 newEffects. push ( ArgumentEffect ( . escaping( toSelection, exclusive) , selectedArg: fromSelection) )
143156 return true
@@ -184,52 +197,70 @@ private func isOperandOfRecursiveCall(_ op: Operand) -> Bool {
184197/// there are no other arguments or escape points than `fromArgument`. Also, the
185198/// path at the `fromArgument` must match with `fromPath`.
186199private
187- func isExclusiveEscape( fromArgument: Argument , fromPath: Path , to toSelection: Selection ,
188- _ returnInst: ReturnInst , _ escapeInfo : inout EscapeInfo ) -> Bool {
200+ func isExclusiveEscape( context : PassContext , fromArgument: Argument , fromPath: Path , to toSelection: Selection ,
201+ _ returnInst: ReturnInst ) -> Bool {
189202 switch toSelection. value {
190203
191204 // argument -> return
192205 case . returnValue:
193- if escapeInfo. isEscaping (
194- object: returnInst. operand, path: toSelection. pathPattern,
195- visitUse: { op, path, followStores in
196- if op. instruction == returnInst {
197- if followStores { return . markEscaping }
198- if path. matches ( pattern: toSelection. pathPattern) {
199- return . ignore
200- }
201- return . markEscaping
202- }
203- return . continueWalking
204- } ,
205- visitDef: { def, path, followStores in
206- guard let arg = def as? FunctionArgument else {
207- return . continueWalkingUp
208- }
209- if followStores { return . markEscaping }
210- if arg == fromArgument && path. matches ( pattern: fromPath) {
211- return . continueWalkingDown
212- }
213- return . markEscaping
214- } ) {
206+ struct IsExclusiveReturnEscapeVisitor : EscapeInfoVisitor {
207+ let fromArgument : Argument
208+ let toSelection : Selection
209+ let returnInst : ReturnInst
210+ let fromPath : Path
211+
212+ mutating func visitUse( operand: Operand , path: Path , state: State ) -> UseResult {
213+ if operand. instruction == returnInst {
214+ if state. followStores { return . abort }
215+ if path. matches ( pattern: toSelection. pathPattern) {
216+ return . ignore
217+ }
218+ return . abort
219+ }
220+ return . continueWalk
221+ }
222+
223+ mutating func visitDef( def: Value , path: Path , state: State ) -> DefResult {
224+ guard let arg = def as? FunctionArgument else {
225+ return . continueWalkUp
226+ }
227+ if state. followStores { return . abort }
228+ if arg == fromArgument && path. matches ( pattern: fromPath) {
229+ return . walkDown
230+ }
231+ return . abort
232+ }
233+ }
234+ let visitor = IsExclusiveReturnEscapeVisitor ( fromArgument: fromArgument, toSelection: toSelection, returnInst: returnInst, fromPath: fromPath)
235+ var walker = EscapeInfo ( calleeAnalysis: context. calleeAnalysis, visitor: visitor)
236+ if walker. isEscaping ( object: returnInst. operand, path: toSelection. pathPattern) {
215237 return false
216238 }
217-
218239 // argument -> argument
219240 case . argument( let toArgIdx) :
241+ struct IsExclusiveArgumentEscapeVisitor : EscapeInfoVisitor {
242+ let fromArgument : Argument
243+ let fromPath : Path
244+ let toSelection : Selection
245+ let toArg : FunctionArgument
246+
247+ mutating func visitDef( def: Value , path: Path , state: State ) -> DefResult {
248+ guard let arg = def as? FunctionArgument else {
249+ return . continueWalkUp
250+ }
251+ if state. followStores { return . abort }
252+ if arg == fromArgument && path. matches ( pattern: fromPath) { return . walkDown }
253+ if arg == toArg && path. matches ( pattern: toSelection. pathPattern) { return . walkDown }
254+ return . abort
255+ }
256+ }
220257 let toArg = returnInst. function. arguments [ toArgIdx]
221- if escapeInfo. isEscaping ( object: toArg, path: toSelection. pathPattern,
222- visitDef: { def, path, followStores in
223- guard let arg = def as? FunctionArgument else {
224- return . continueWalkingUp
225- }
226- if followStores { return . markEscaping }
227- if arg == fromArgument && path. matches ( pattern: fromPath) { return . continueWalkingDown }
228- if arg == toArg && path. matches ( pattern: toSelection. pathPattern) { return . continueWalkingDown }
229- return . markEscaping
230- } ) {
258+ let visitor = IsExclusiveArgumentEscapeVisitor ( fromArgument: fromArgument, fromPath: fromPath, toSelection: toSelection, toArg: toArg)
259+ var walker = EscapeInfo ( calleeAnalysis: context. calleeAnalysis, visitor: visitor)
260+ if walker. isEscaping ( object: toArg, path: toSelection. pathPattern) {
231261 return false
232262 }
233263 }
234264 return true
235265}
266+
0 commit comments