@@ -60,4 +60,148 @@ struct AliasAnalysis {
60
60
// Any other non-address value means: all addresses of any referenced class instances within the value.
61
61
return SmallProjectionPath ( . anyValueFields) . push ( . anyClassField) . push ( . anyValueFields)
62
62
}
63
+
64
+ static func register( ) {
65
+ AliasAnalysis_register (
66
+ // getMemEffectsFn
67
+ { ( bridgedCtxt: BridgedPassContext , bridgedVal: BridgedValue , bridgedInst: BridgedInstruction ) -> BridgedMemoryBehavior in
68
+ let context = PassContext ( _bridged: bridgedCtxt)
69
+ let inst = bridgedInst. instruction
70
+ let val = bridgedVal. value
71
+ let path = AliasAnalysis . getPtrOrAddressPath ( for: val)
72
+ if let apply = inst as? ApplySite {
73
+ let effect = getMemoryEffect ( of: apply, for: val, path: path, context)
74
+ switch ( effect. read, effect. write) {
75
+ case ( false , false ) : return NoneBehavior
76
+ case ( true , false ) : return MayReadBehavior
77
+ case ( false , true ) : return MayWriteBehavior
78
+ case ( true , true ) : return MayReadWriteBehavior
79
+ }
80
+ }
81
+ if val. at ( path) . isAddressEscaping ( using: EscapesToInstructionVisitor ( target: inst) , context) {
82
+ return MayReadWriteBehavior
83
+ }
84
+ return NoneBehavior
85
+ } ,
86
+
87
+ // isObjReleasedFn
88
+ { ( bridgedCtxt: BridgedPassContext , bridgedObj: BridgedValue , bridgedInst: BridgedInstruction ) -> Bool in
89
+ let context = PassContext ( _bridged: bridgedCtxt)
90
+ let inst = bridgedInst. instruction
91
+ let obj = bridgedObj. value
92
+ let path = SmallProjectionPath ( . anyValueFields)
93
+ if let apply = inst as? ApplySite {
94
+ let effect = getOwnershipEffect ( of: apply, for: obj, path: path, context)
95
+ return effect. destroy
96
+ }
97
+ return obj. at ( path) . isEscaping ( using: EscapesToInstructionVisitor ( target: inst) , context)
98
+ } ,
99
+
100
+ // isAddrVisibleFromObj
101
+ { ( bridgedCtxt: BridgedPassContext , bridgedAddr: BridgedValue , bridgedObj: BridgedValue ) -> Bool in
102
+ let context = PassContext ( _bridged: bridgedCtxt)
103
+ let addr = bridgedAddr. value. at ( AliasAnalysis . getPtrOrAddressPath ( for: bridgedAddr. value) )
104
+
105
+ // This is similar to `canReferenceSameFieldFn`, except that all addresses of all objects are
106
+ // considered which are transitively visible from `bridgedObj`.
107
+ let anythingReachableFromObj = bridgedObj. value. at ( SmallProjectionPath ( . anything) )
108
+ return addr. canAddressAlias ( with: anythingReachableFromObj, context)
109
+ } ,
110
+
111
+ // canReferenceSameFieldFn
112
+ { ( bridgedCtxt: BridgedPassContext , bridgedLhs: BridgedValue , bridgedRhs: BridgedValue ) -> Bool in
113
+ let context = PassContext ( _bridged: bridgedCtxt)
114
+
115
+ // If `lhs` or `rhs` is not an address, but an object, it means: check for alias of any class
116
+ // field address of the object.
117
+ let lhs = bridgedLhs. value. at ( AliasAnalysis . getPtrOrAddressPath ( for: bridgedLhs. value) )
118
+ let rhs = bridgedRhs. value. at ( AliasAnalysis . getPtrOrAddressPath ( for: bridgedRhs. value) )
119
+ return lhs. canAddressAlias ( with: rhs, context)
120
+ }
121
+ )
122
+ }
123
+ }
124
+
125
+ private func getMemoryEffect( of apply: ApplySite , for address: Value , path: SmallProjectionPath , _ context: PassContext ) -> SideEffects . Memory {
126
+ let calleeAnalysis = context. calleeAnalysis
127
+ let visitor = SideEffectsVisitor ( apply: apply, calleeAnalysis: calleeAnalysis)
128
+ let memoryEffects : SideEffects . Memory
129
+
130
+ // First try to figure out to which argument(s) the address "escapes" to.
131
+ if let result = address. at ( path) . visitAddress ( using: visitor, context) {
132
+ // The resulting effects are the argument effects to which `address` escapes to.
133
+ memoryEffects = result. memory
134
+ } else {
135
+ // `address` has unknown escapes. So we have to take the global effects of the called function(s).
136
+ memoryEffects = calleeAnalysis. getSideEffects ( of: apply) . memory
137
+ }
138
+ // Do some magic for `let` variables. Function calls cannot modify let variables.
139
+ // The only exception is that the let variable is directly passed to an indirect out of the
140
+ // apply.
141
+ // TODO: make this a more formal and verified approach.
142
+ if memoryEffects. write && address. accessBase. isLet && !address. isIndirectResult ( of: apply) {
143
+ return SideEffects . Memory ( read: memoryEffects. read, write: false )
144
+ }
145
+ return memoryEffects
146
+ }
147
+
148
+ private func getOwnershipEffect( of apply: ApplySite , for value: Value , path: SmallProjectionPath , _ context: PassContext ) -> SideEffects . Ownership {
149
+ let visitor = SideEffectsVisitor ( apply: apply, calleeAnalysis: context. calleeAnalysis)
150
+ if let result = value. at ( path) . visit ( using: visitor, context) {
151
+ // The resulting effects are the argument effects to which `value` escapes to.
152
+ return result. ownership
153
+ } else {
154
+ // `value` has unknown escapes. So we have to take the global effects of the called function(s).
155
+ return visitor. calleeAnalysis. getSideEffects ( of: apply) . ownership
156
+ }
157
+ }
158
+
159
+ private struct SideEffectsVisitor : EscapeVisitorWithResult {
160
+ let apply : ApplySite
161
+ let calleeAnalysis : CalleeAnalysis
162
+ var result = SideEffects . GlobalEffects ( )
163
+
164
+ mutating func visitUse( operand: Operand , path: EscapePath ) -> UseResult {
165
+ let user = operand. instruction
166
+ if user is ReturnInst {
167
+ // Anything which is returned cannot escape to an instruction inside the function.
168
+ return . ignore
169
+ }
170
+ if user == apply {
171
+ if let argIdx = apply. argumentIndex ( of: operand) {
172
+ let e = calleeAnalysis. getSideEffects ( of: apply, forArgument: argIdx, path: path. projectionPath)
173
+ result. merge ( with: e)
174
+ }
175
+ }
176
+ return . continueWalk
177
+ }
178
+ }
179
+
180
+ private extension Value {
181
+ /// Returns true if this address is passed as indirect out of `apply`.
182
+ func isIndirectResult( of apply: ApplySite ) -> Bool {
183
+ guard let fullApply = apply as? FullApplySite else {
184
+ return false
185
+ }
186
+ if fullApply. numIndirectResultArguments == 0 {
187
+ return false
188
+ }
189
+
190
+ var walker = IsIndirectResultWalker ( apply: fullApply)
191
+ return walker. walkDownUses ( ofAddress: self , path: UnusedWalkingPath ( ) ) == . abortWalk
192
+ }
193
+ }
194
+
195
+ private struct IsIndirectResultWalker : AddressDefUseWalker {
196
+ let apply : FullApplySite
197
+
198
+ mutating func leafUse( address: Operand , path: UnusedWalkingPath ) -> WalkResult {
199
+ if address. instruction == apply,
200
+ let argIdx = apply. argumentIndex ( of: address) ,
201
+ argIdx < apply. numIndirectResultArguments {
202
+ return . abortWalk
203
+ }
204
+ return . continueWalk
205
+ }
63
206
}
207
+
0 commit comments