@@ -296,13 +296,15 @@ private func collectMovableInstructions(
296296 continue
297297 }
298298
299- if !analyzedInstructions. sideEffectsMayRelease || !analyzedInstructions. loopSideEffectsMayWriteTo ( address: fixLifetimeInst. operand. value, context. aliasAnalysis) {
299+ if !analyzedInstructions. sideEffectsMayRelease ||
300+ !analyzedInstructions. sideEffectsMayWrite ( to: fixLifetimeInst. operand. value, context. aliasAnalysis)
301+ {
300302 movableInstructions. sinkDown. append ( fixLifetimeInst)
301303 }
302304 case let loadInst as LoadInst :
303305 // Avoid quadratic complexity in corner cases. Usually, this limit will not be exceeded.
304306 if loadInstCounter * analyzedInstructions. loopSideEffects. count < 8000 ,
305- !analyzedInstructions. loopSideEffectsMayWriteTo ( address : loadInst. operand . value , context. aliasAnalysis) {
307+ !analyzedInstructions. sideEffectsMayWrite ( to : loadInst. address , context. aliasAnalysis) {
306308 movableInstructions. hoistUp. append ( loadInst)
307309 }
308310
@@ -532,13 +534,41 @@ private extension AnalyzedInstructions {
532534
533535 /// Returns true if `loopSideEffects` contains any memory writes which
534536 /// may alias with the memory `address`.
535- func loopSideEffectsMayWriteTo ( address: Value , _ aliasAnalysis: AliasAnalysis ) -> Bool {
537+ func sideEffectsMayWrite ( to address: Value , _ aliasAnalysis: AliasAnalysis ) -> Bool {
536538 return loopSideEffects
537539 . contains { sideEffect in
538540 sideEffect. mayWrite ( toAddress: address, aliasAnalysis)
539541 }
540542 }
541-
543+
544+ func sideEffectsMayWrite( to address: Value ,
545+ outsideOf scope: InstructionRange ,
546+ _ context: FunctionPassContext
547+ ) -> Bool {
548+ for sideEffectInst in loopSideEffects {
549+ if sideEffectInst. mayWrite ( toAddress: address, context. aliasAnalysis) ,
550+ !scope. inclusiveRangeContains ( sideEffectInst)
551+ {
552+ return true
553+ }
554+ }
555+ return false
556+ }
557+
558+ func sideEffectsMayReadOrWrite( to address: Value ,
559+ outsideOf scope: InstructionRange ,
560+ _ context: FunctionPassContext
561+ ) -> Bool {
562+ for sideEffectInst in loopSideEffects {
563+ if sideEffectInst. mayReadOrWrite ( address: address, context. aliasAnalysis) ,
564+ !scope. inclusiveRangeContains ( sideEffectInst)
565+ {
566+ return true
567+ }
568+ }
569+ return false
570+ }
571+
542572 /// Find all loads that contain `accessPath`. Split them into a load with
543573 /// identical `accessPath` and a set of non-overlapping loads. Add the new
544574 /// non-overlapping loads to `loads`.
@@ -1206,47 +1236,19 @@ private extension ScopedInstruction {
12061236 case is BeginApplyInst :
12071237 return true // Has already been checked with other full applies.
12081238 case let loadBorrowInst as LoadBorrowInst :
1209- for sideEffectInst in analyzedInstructions. loopSideEffects {
1210- if let endBorrow = sideEffectInst as? EndBorrowInst ,
1211- let begin = endBorrow. borrow as? LoadBorrowInst ,
1212- begin == self
1213- {
1214- continue
1215- }
1216- if sideEffectInst. mayWrite ( toAddress: loadBorrowInst. address, context. aliasAnalysis) ,
1217- !scope. contains ( sideEffectInst)
1218- {
1219- return false
1220- }
1221- }
1222- return true
1239+ return !analyzedInstructions. sideEffectsMayWrite ( to: loadBorrowInst. address, outsideOf: scope, context)
12231240
12241241 case let beginAccess as BeginAccessInst :
1225- for fullApplyInst in analyzedInstructions. fullApplies {
1226- guard mayWriteToMemory && fullApplyInst. mayReadOrWrite ( address: beginAccess. address, context. aliasAnalysis) ||
1227- !mayWriteToMemory && fullApplyInst. mayWrite ( toAddress: beginAccess. address, context. aliasAnalysis) else {
1228- continue
1229- }
1230-
1231- // After hoisting the begin/end_access the apply will be within the scope, so it must not have a conflicting access.
1232- if !scope. contains ( fullApplyInst) {
1233- return false
1234- }
1235- }
1236-
1237- switch beginAccess. address. accessPath. base {
1238- case . class, . global:
1239- for sideEffect in analyzedInstructions. loopSideEffects where sideEffect. mayRelease {
1240- // Since a class might have a deinitializer, hoisting begin/end_access pair could violate
1241- // exclusive access if the deinitializer accesses address used by begin_access.
1242- if !scope. contains ( sideEffect) {
1243- return false
1244- }
1245- }
1246-
1247- return true
1248- default :
1249- return true
1242+ if beginAccess. accessKind == . read {
1243+ // Check that we don't generate nested accesses when extending the access scope. Also, we must not
1244+ // extend a "read" access scope over a memory write (to the same address) even if the write is _not_
1245+ // in an access scope, because this would confuse alias analysis.
1246+ return !analyzedInstructions. sideEffectsMayWrite ( to: beginAccess. address, outsideOf: scope, context)
1247+ } else {
1248+ // This does not include memory-reading instructions which are not in `loopSideEffects`, like a
1249+ // plain `load`. This is fine because we can extend a "modify" access scope over memory reads
1250+ // (of the same address) as long as we are not generating nested accesses.
1251+ return !analyzedInstructions. sideEffectsMayReadOrWrite ( to: beginAccess. address, outsideOf: scope, context)
12501252 }
12511253 case is BeginBorrowInst , is StoreBorrowInst :
12521254 // Ensure the value is produced outside the loop.
0 commit comments