1212
1313import SIL
1414
15- fileprivate typealias Selection = ArgumentEffect . Selection
16-
1715/// Computes effects for function arguments.
1816///
1917/// For example, if an argument does not escape, adds a non-escaping effect,
@@ -50,17 +48,16 @@ let computeEffects = FunctionPass(name: "compute-effects", {
5048
5149 // First check: is the argument (or a projected value of it) escaping at all?
5250 if !escapeInfo. isEscapingWhenWalkingDown ( object: arg, path: SmallProjectionPath ( . anything) ) {
53- let selectedArg = Selection ( arg, pathPattern: SmallProjectionPath ( . anything) )
54- newEffects. push ( ArgumentEffect ( . notEscaping, selectedArg: selectedArg) )
51+ newEffects. push ( ArgumentEffect ( . notEscaping, argumentIndex: arg. index, pathPattern: SmallProjectionPath ( . anything) ) )
5552 continue
5653 }
5754
5855 // Now compute effects for two important cases:
5956 // * the argument itself + any value projections, and...
60- if addArgEffects ( context : context , arg, argPath: SmallProjectionPath ( ) , to: & newEffects, returnInst) {
57+ if addArgEffects ( arg, argPath: SmallProjectionPath ( ) , to: & newEffects, returnInst, context ) {
6158 // * single class indirections
62- _ = addArgEffects ( context : context , arg, argPath: SmallProjectionPath ( . anyValueFields) . push ( . anyClassField) ,
63- to: & newEffects, returnInst)
59+ _ = addArgEffects ( arg, argPath: SmallProjectionPath ( . anyValueFields) . push ( . anyClassField) ,
60+ to: & newEffects, returnInst, context )
6461 }
6562 }
6663
@@ -74,34 +71,35 @@ let computeEffects = FunctionPass(name: "compute-effects", {
7471
7572/// Returns true if an argument effect was added.
7673private
77- func addArgEffects( context : PassContext , _ arg: FunctionArgument , argPath ap: SmallProjectionPath ,
74+ func addArgEffects( _ arg: FunctionArgument , argPath ap: SmallProjectionPath ,
7875 to newEffects: inout Stack < ArgumentEffect > ,
79- _ returnInst: ReturnInst ? ) -> Bool {
76+ _ returnInst: ReturnInst ? , _ context : PassContext ) -> Bool {
8077 // Correct the path if the argument is not a class reference itself, but a value type
8178 // containing one or more references.
8279 let argPath = arg. type. isClass ? ap : ap. push ( . anyValueFields)
8380
8481 struct ArgEffectsVisitor : EscapeInfoVisitor {
85- init ( toSelection: Selection ? , returnInst: ReturnInst ? ) {
86- self . toSelection = toSelection
87- self . returnInst = returnInst
82+ enum EscapeDestination {
83+ case notSet
84+ case toReturn( SmallProjectionPath )
85+ case toArgument( Int , SmallProjectionPath ) // argument index, path
8886 }
89-
90- var toSelection : Selection ?
91- var returnInst : ReturnInst ?
87+ var result = EscapeDestination . notSet
9288
9389 mutating func visitUse( operand: Operand , path: EscapePath ) -> UseResult {
94- if operand. instruction == returnInst {
90+ if operand. instruction is ReturnInst {
9591 // The argument escapes to the function return
9692 if path. followStores {
9793 // The escaping path must not introduce a followStores.
9894 return . abort
9995 }
100- if let ta = toSelection {
101- if ta. value != . returnValue { return . abort }
102- toSelection = Selection ( . returnValue, pathPattern: path. projectionPath. merge ( with: ta. pathPattern) )
103- } else {
104- toSelection = Selection ( . returnValue, pathPattern: path. projectionPath)
96+ switch result {
97+ case . notSet:
98+ result = . toReturn( path. projectionPath)
99+ case . toReturn( let oldPath) :
100+ result = . toReturn( oldPath. merge ( with: path. projectionPath) )
101+ case . toArgument:
102+ return . abort
105103 }
106104 return . ignore
107105 }
@@ -121,37 +119,44 @@ func addArgEffects(context: PassContext, _ arg: FunctionArgument, argPath ap: Sm
121119 return . abort
122120 }
123121 let argIdx = destArg. index
124- if let ta = toSelection {
125- if ta. value != . argument( argIdx) { return . abort }
126- toSelection = Selection ( . argument( argIdx) , pathPattern: path. projectionPath. merge ( with: ta. pathPattern) )
127- } else {
128- toSelection = Selection ( . argument( argIdx) , pathPattern: path. projectionPath)
122+ switch result {
123+ case . notSet:
124+ result = . toArgument( argIdx, path. projectionPath)
125+ case . toArgument( let oldArgIdx, let oldPath) where oldArgIdx == argIdx:
126+ result = . toArgument( argIdx, oldPath. merge ( with: path. projectionPath) )
127+ default :
128+ return . abort
129129 }
130130 return . walkDown
131131 }
132132 }
133133
134- var walker = EscapeInfo ( calleeAnalysis: context. calleeAnalysis, visitor: ArgEffectsVisitor ( toSelection : nil , returnInst : returnInst ) )
134+ var walker = EscapeInfo ( calleeAnalysis: context. calleeAnalysis, visitor: ArgEffectsVisitor ( ) )
135135 if walker. isEscapingWhenWalkingDown ( object: arg, path: argPath) {
136136 return false
137137 }
138138
139- let toSelection = walker. visitor. toSelection
140- let fromSelection = Selection ( arg, pathPattern: argPath)
141-
142- guard let toSelection = toSelection else {
143- newEffects. push ( ArgumentEffect ( . notEscaping, selectedArg: fromSelection) )
144- return true
145- }
146-
147139 // If the function never returns, the argument can not escape to another arg/return.
148- guard let returnInst = returnInst else {
140+ guard let returnInst = arg . function . returnInstruction else {
149141 return false
150142 }
151143
152- let exclusive = isExclusiveEscape ( context: context, fromArgument: arg, fromPath: argPath, to: toSelection, returnInst)
153-
154- newEffects. push ( ArgumentEffect ( . escaping( toSelection, exclusive) , selectedArg: fromSelection) )
144+ let effect : ArgumentEffect
145+ switch walker. visitor. result {
146+ case . notSet:
147+ effect = ArgumentEffect ( . notEscaping, argumentIndex: arg. index, pathPattern: argPath)
148+ case . toReturn( let toPath) :
149+ let exclusive = isExclusiveEscapeToReturn ( fromArgument: arg, fromPath: argPath,
150+ toPath: toPath, returnInst: returnInst, context)
151+ effect = ArgumentEffect ( . escapingToReturn( toPath, exclusive) ,
152+ argumentIndex: arg. index, pathPattern: argPath)
153+ case . toArgument( let toArgIdx, let toPath) :
154+ let exclusive = isExclusiveEscapeToArgument ( fromArgument: arg, fromPath: argPath,
155+ toArgumentIndex: toArgIdx, toPath: toPath, context)
156+ effect = ArgumentEffect ( . escapingToArgument( toArgIdx, toPath, exclusive) ,
157+ argumentIndex: arg. index, pathPattern: argPath)
158+ }
159+ newEffects. push ( effect)
155160 return true
156161}
157162
@@ -162,17 +167,13 @@ private func getArgIndicesWithDefinedEffects(of function: Function) -> Set<Int>
162167 for effect in function. effects. argumentEffects {
163168 if effect. isDerived { continue }
164169
165- if case . argument( let argIdx) = effect. selectedArg. value {
166- argsWithDefinedEffects. insert ( argIdx)
167- }
170+ argsWithDefinedEffects. insert ( effect. argumentIndex)
168171
169172 switch effect. kind {
170- case . notEscaping:
173+ case . notEscaping, . escapingToReturn :
171174 break
172- case . escaping( let to, _) :
173- if case . argument( let toArgIdx) = to. value {
174- argsWithDefinedEffects. insert ( toArgIdx)
175- }
175+ case . escapingToArgument( let toArgIdx, _, _) :
176+ argsWithDefinedEffects. insert ( toArgIdx)
176177 }
177178 }
178179 return argsWithDefinedEffects
@@ -196,70 +197,64 @@ private func isOperandOfRecursiveCall(_ op: Operand) -> Bool {
196197/// there are no other arguments or escape points than `fromArgument`. Also, the
197198/// path at the `fromArgument` must match with `fromPath`.
198199private
199- func isExclusiveEscape( context: PassContext , fromArgument: Argument , fromPath: SmallProjectionPath , to toSelection: Selection ,
200- _ returnInst: ReturnInst ) -> Bool {
201- switch toSelection. value {
202-
203- // argument -> return
204- case . returnValue:
205- struct IsExclusiveReturnEscapeVisitor : EscapeInfoVisitor {
206- let fromArgument : Argument
207- let toSelection : Selection
208- let returnInst : ReturnInst
209- let fromPath : SmallProjectionPath
210-
211- mutating func visitUse( operand: Operand , path: EscapePath ) -> UseResult {
212- if operand. instruction == returnInst {
213- if path. followStores { return . abort }
214- if path. projectionPath. matches ( pattern: toSelection. pathPattern) {
215- return . ignore
216- }
217- return . abort
218- }
219- return . continueWalk
220- }
221-
222- mutating func visitDef( def: Value , path: EscapePath ) -> DefResult {
223- guard let arg = def as? FunctionArgument else {
224- return . continueWalkUp
225- }
200+ func isExclusiveEscapeToReturn( fromArgument: Argument , fromPath: SmallProjectionPath ,
201+ toPath: SmallProjectionPath ,
202+ returnInst: ReturnInst , _ context: PassContext ) -> Bool {
203+ struct IsExclusiveReturnEscapeVisitor : EscapeInfoVisitor {
204+ let fromArgument : Argument
205+ let fromPath : SmallProjectionPath
206+ let toPath : SmallProjectionPath
207+
208+ mutating func visitUse( operand: Operand , path: EscapePath ) -> UseResult {
209+ if operand. instruction is ReturnInst {
226210 if path. followStores { return . abort }
227- if arg == fromArgument && path. projectionPath. matches ( pattern: fromPath ) {
228- return . walkDown
211+ if path. projectionPath. matches ( pattern: toPath ) {
212+ return . ignore
229213 }
230214 return . abort
231215 }
216+ return . continueWalk
232217 }
233- let visitor = IsExclusiveReturnEscapeVisitor ( fromArgument: fromArgument, toSelection: toSelection, returnInst: returnInst, fromPath: fromPath)
234- var walker = EscapeInfo ( calleeAnalysis: context. calleeAnalysis, visitor: visitor)
235- if walker. isEscaping ( object: returnInst. operand, path: toSelection. pathPattern) {
236- return false
237- }
238- // argument -> argument
239- case . argument( let toArgIdx) :
240- struct IsExclusiveArgumentEscapeVisitor : EscapeInfoVisitor {
241- let fromArgument : Argument
242- let fromPath : SmallProjectionPath
243- let toSelection : Selection
244- let toArg : FunctionArgument
245-
246- mutating func visitDef( def: Value , path: EscapePath ) -> DefResult {
247- guard let arg = def as? FunctionArgument else {
248- return . continueWalkUp
249- }
250- if path. followStores { return . abort }
251- if arg == fromArgument && path. projectionPath. matches ( pattern: fromPath) { return . walkDown }
252- if arg == toArg && path. projectionPath. matches ( pattern: toSelection. pathPattern) { return . walkDown }
253- return . abort
218+
219+ mutating func visitDef( def: Value , path: EscapePath ) -> DefResult {
220+ guard let arg = def as? FunctionArgument else {
221+ return . continueWalkUp
222+ }
223+ if path. followStores { return . abort }
224+ if arg == fromArgument && path. projectionPath. matches ( pattern: fromPath) {
225+ return . walkDown
254226 }
227+ return . abort
255228 }
256- let toArg = returnInst. function. arguments [ toArgIdx]
257- let visitor = IsExclusiveArgumentEscapeVisitor ( fromArgument: fromArgument, fromPath: fromPath, toSelection: toSelection, toArg: toArg)
258- var walker = EscapeInfo ( calleeAnalysis: context. calleeAnalysis, visitor: visitor)
259- if walker. isEscaping ( object: toArg, path: toSelection. pathPattern) {
260- return false
229+ }
230+ let visitor = IsExclusiveReturnEscapeVisitor ( fromArgument: fromArgument, fromPath: fromPath, toPath: toPath)
231+ var walker = EscapeInfo ( calleeAnalysis: context. calleeAnalysis, visitor: visitor)
232+ return !walker. isEscaping ( object: returnInst. operand, path: toPath)
233+ }
234+
235+ private
236+ func isExclusiveEscapeToArgument( fromArgument: Argument , fromPath: SmallProjectionPath ,
237+ toArgumentIndex: Int , toPath: SmallProjectionPath , _ context: PassContext ) -> Bool {
238+ struct IsExclusiveArgumentEscapeVisitor : EscapeInfoVisitor {
239+ let fromArgument : Argument
240+ let fromPath : SmallProjectionPath
241+ let toArgumentIndex : Int
242+ let toPath : SmallProjectionPath
243+
244+ mutating func visitDef( def: Value , path: EscapePath ) -> DefResult {
245+ guard let arg = def as? FunctionArgument else {
246+ return . continueWalkUp
247+ }
248+ if path. followStores { return . abort }
249+ if arg == fromArgument && path. projectionPath. matches ( pattern: fromPath) { return . walkDown }
250+ if arg. index == toArgumentIndex && path. projectionPath. matches ( pattern: toPath) { return . walkDown }
251+ return . abort
261252 }
262253 }
263- return true
254+ let visitor = IsExclusiveArgumentEscapeVisitor ( fromArgument: fromArgument, fromPath: fromPath,
255+ toArgumentIndex: toArgumentIndex, toPath: toPath)
256+ var walker = EscapeInfo ( calleeAnalysis: context. calleeAnalysis, visitor: visitor)
257+ let toArg = fromArgument. function. arguments [ toArgumentIndex]
258+ return !walker. isEscaping ( object: toArg, path: toPath)
264259}
265260
0 commit comments