@@ -60,4 +60,148 @@ struct AliasAnalysis {
6060 // Any other non-address value means: all addresses of any referenced class instances within the value.
6161 return SmallProjectionPath ( . anyValueFields) . push ( . anyClassField) . push ( . anyValueFields)
6262 }
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+ }
63206}
207+
0 commit comments