From ad2462f7f208c535af9de38fe847fc484d2da942 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 6 Feb 2025 14:09:07 +0100 Subject: [PATCH 1/8] RedundantLoadElimination: optimize a load from a memory location which was written by copy_addr For example: ``` %1 = alloc_stack $B copy_addr %0 to [init] %1 %3 = load [take] %1 dealloc_stack %1 ``` -> ``` %3 = load [copy] %0 ``` --- .../RedundantLoadElimination.swift | 58 ++++++++- .../Optimizer/Utilities/OptUtils.swift | 26 ++++ test/SILOptimizer/redundant_load_elim.sil | 45 +++++++ .../SILOptimizer/redundant_load_elim_ossa.sil | 121 ++++++++++++++++++ 4 files changed, 245 insertions(+), 5 deletions(-) diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift index d92d7983bbee5..5848ad65759ad 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift @@ -251,7 +251,7 @@ private func replace(load: LoadInst, with availableValues: [AvailableValue], _ c var ssaUpdater = SSAUpdater(function: load.parentFunction, type: load.type, ownership: load.ownership, context) - for availableValue in availableValues { + for availableValue in availableValues.replaceCopyAddrsWithLoadsAndStores(context) { let block = availableValue.instruction.parentBlock let availableValue = provideValue(for: load, from: availableValue, context) ssaUpdater.addAvailableValue(availableValue, in: block) @@ -342,6 +342,8 @@ private func shrinkMemoryLifetime(from load: LoadInst, to availableValue: Availa fatalError("unqualified store in ossa function?") } return valueToAdd + case .viaCopyAddr: + fatalError("copy_addr must be lowered before shrinking lifetime") } } @@ -380,6 +382,8 @@ private func shrinkMemoryLifetimeAndSplit(from load: LoadInst, to availableValue let valueToAdd = builder.createLoad(fromAddress: addr, ownership: .take) availableStore.trySplit(context) return valueToAdd + case .viaCopyAddr: + fatalError("copy_addr must be lowered before shrinking lifetime") } } @@ -387,25 +391,29 @@ private func shrinkMemoryLifetimeAndSplit(from load: LoadInst, to availableValue private enum AvailableValue { case viaLoad(LoadInst) case viaStore(StoreInst) + case viaCopyAddr(CopyAddrInst) var value: Value { switch self { case .viaLoad(let load): return load case .viaStore(let store): return store.source + case .viaCopyAddr: fatalError("copy_addr must be lowered") } } var address: Value { switch self { - case .viaLoad(let load): return load.address - case .viaStore(let store): return store.destination + case .viaLoad(let load): return load.address + case .viaStore(let store): return store.destination + case .viaCopyAddr(let copyAddr): return copyAddr.destination } } var instruction: Instruction { switch self { - case .viaLoad(let load): return load - case .viaStore(let store): return store + case .viaLoad(let load): return load + case .viaStore(let store): return store + case .viaCopyAddr(let copyAddr): return copyAddr } } @@ -413,6 +421,19 @@ private enum AvailableValue { switch self { case .viaLoad(let load): return Builder(after: load, context) case .viaStore(let store): return Builder(before: store, context) + case .viaCopyAddr: fatalError("copy_addr must be lowered") + } + } +} + +private extension Array where Element == AvailableValue { + func replaceCopyAddrsWithLoadsAndStores(_ context: FunctionPassContext) -> [AvailableValue] { + return map { + if case .viaCopyAddr(let copyAddr) = $0 { + return .viaStore(copyAddr.replaceWithLoadAndStore(context)) + } else { + return $0 + } } } } @@ -520,6 +541,16 @@ private struct InstructionScanner { potentiallyRedundantSubpath = precedingStorePath } + case let preceedingCopy as CopyAddrInst where preceedingCopy.canProvideValue: + let copyPath = preceedingCopy.destination.constantAccessPath + if copyPath.getMaterializableProjection(to: accessPath) != nil { + availableValues.append(.viaCopyAddr(preceedingCopy)) + return .available + } + if accessPath.getMaterializableProjection(to: copyPath) != nil, potentiallyRedundantSubpath == nil { + potentiallyRedundantSubpath = copyPath + } + default: break } @@ -606,3 +637,20 @@ private struct Liverange { return false } } + +private extension CopyAddrInst { + var canProvideValue: Bool { + if !source.type.isLoadable(in: parentFunction) { + // Although the original load's type is loadable (obviously), it can be projected-out + // from the copy_addr's type which might be not loadable. + return false + } + if !parentFunction.hasOwnership { + if !isTakeOfSrc || !isInitializationOfDest { + // For simplicity, bail if we would have to insert compensating retains and releases. + return false + } + } + return true + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index 05be31e863f76..df10e274b3f70 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -863,6 +863,32 @@ extension CheckedCastAddrBranchInst { } } +extension CopyAddrInst { + @discardableResult + func replaceWithLoadAndStore(_ context: some MutatingContext) -> StoreInst { + let loadOwnership: LoadInst.LoadOwnership + let storeOwnership: StoreInst.StoreOwnership + if parentFunction.hasOwnership { + if source.type.isTrivial(in: parentFunction) { + loadOwnership = .trivial + storeOwnership = .trivial + } else { + loadOwnership = isTakeOfSrc ? .take : .copy + storeOwnership = isInitializationOfDest ? .initialize : .assign + } + } else { + loadOwnership = .unqualified + storeOwnership = .unqualified + } + + let builder = Builder(before: self, context) + let value = builder.createLoad(fromAddress: source, ownership: loadOwnership) + let store = builder.createStore(source: value, destination: destination, ownership: storeOwnership) + context.erase(instruction: self) + return store + } +} + extension Type { /// True if a type can be expanded without a significant increase to code /// size. diff --git a/test/SILOptimizer/redundant_load_elim.sil b/test/SILOptimizer/redundant_load_elim.sil index 3fc44d5055df9..df3b635ea51a4 100644 --- a/test/SILOptimizer/redundant_load_elim.sil +++ b/test/SILOptimizer/redundant_load_elim.sil @@ -1341,3 +1341,48 @@ bb0(%0 : @guaranteed $COpt, %1 : $Int32): } +// CHECK-LABEL: sil @copy_addr_copy_init : +// CHECK: copy_addr +// CHECK-NEXT: load +// CHECK-LABEL: } // end sil function 'copy_addr_copy_init' +sil @copy_addr_copy_init : $@convention(thin) (@in_guaranteed B, @inout B) -> @owned B { +bb0(%0 : $*B, %1 : $*B): + copy_addr %0 to [init] %1 + %3 = load %1 + return %3 +} + +// CHECK-LABEL: sil @copy_addr_copy_assign : +// CHECK: copy_addr +// CHECK-NEXT: load +// CHECK-LABEL: } // end sil function 'copy_addr_copy_assign' +sil @copy_addr_copy_assign : $@convention(thin) (@in_guaranteed B, @inout B) -> @owned B { +bb0(%0 : $*B, %1 : $*B): + copy_addr %0 to %1 + %3 = load %1 + return %3 +} + +// CHECK-LABEL: sil @copy_addr_take_init : +// CHECK: [[LD:%.*]] = load %0 +// CHECK-NEXT: store [[LD]] to %1 +// CHECK: return [[LD]] +// CHECK-LABEL: } // end sil function 'copy_addr_take_init' +sil @copy_addr_take_init : $@convention(thin) (@in B, @inout B) -> @owned B { +bb0(%0 : $*B, %1 : $*B): + copy_addr [take] %0 to [init] %1 + %3 = load %1 + return %3 +} + +// CHECK-LABEL: sil @copy_addr_take_assign : +// CHECK: copy_addr +// CHECK-NEXT: load +// CHECK-LABEL: } // end sil function 'copy_addr_take_assign' +sil @copy_addr_take_assign : $@convention(thin) (@in B, @inout B) -> @owned B { +bb0(%0 : $*B, %1 : $*B): + copy_addr [take] %0 to %1 + %3 = load %1 + return %3 +} + diff --git a/test/SILOptimizer/redundant_load_elim_ossa.sil b/test/SILOptimizer/redundant_load_elim_ossa.sil index 90bac6d44bcea..4d671cb369bce 100644 --- a/test/SILOptimizer/redundant_load_elim_ossa.sil +++ b/test/SILOptimizer/redundant_load_elim_ossa.sil @@ -130,6 +130,13 @@ struct F { } +protocol P {} + +struct ExistentialIntPair { + var p: P + var x: Int +} + sil_global @total : $Int32 sil @use : $@convention(thin) (Builtin.Int32) -> () @@ -1531,3 +1538,117 @@ bb0(%0 : $*B): return %8 : $() } +// CHECK-LABEL: sil [ossa] @copy_addr_copy_init : +// CHECK: [[LD:%.*]] = load [copy] %0 +// CHECK-NEXT: [[C:%.*]] = copy_value [[LD]] +// CHECK-NEXT: store [[LD]] to [init] %1 +// CHECK: return [[C]] +// CHECK-LABEL: } // end sil function 'copy_addr_copy_init' +sil [ossa] @copy_addr_copy_init : $@convention(thin) (@in_guaranteed B, @inout B) -> @owned B { +bb0(%0 : $*B, %1 : $*B): + destroy_addr %1 + copy_addr %0 to [init] %1 + %3 = load [copy] %1 + return %3 +} + +// CHECK-LABEL: sil [ossa] @copy_addr_copy_assign : +// CHECK: [[LD:%.*]] = load [copy] %0 +// CHECK-NEXT: [[C:%.*]] = copy_value [[LD]] +// CHECK-NEXT: store [[LD]] to [assign] %1 +// CHECK: return [[C]] +// CHECK-LABEL: } // end sil function 'copy_addr_copy_assign' +sil [ossa] @copy_addr_copy_assign : $@convention(thin) (@in_guaranteed B, @inout B) -> @owned B { +bb0(%0 : $*B, %1 : $*B): + copy_addr %0 to %1 + %3 = load [copy] %1 + return %3 +} + +// CHECK-LABEL: sil [ossa] @copy_addr_take_init : +// CHECK: [[LD:%.*]] = load [take] %0 +// CHECK-NEXT: [[C:%.*]] = copy_value [[LD]] +// CHECK-NEXT: store [[LD]] to [init] %1 +// CHECK: return [[C]] +// CHECK-LABEL: } // end sil function 'copy_addr_take_init' +sil [ossa] @copy_addr_take_init : $@convention(thin) (@in B, @inout B) -> @owned B { +bb0(%0 : $*B, %1 : $*B): + destroy_addr %1 + copy_addr [take] %0 to [init] %1 + %3 = load [copy] %1 + return %3 +} + +// CHECK-LABEL: sil [ossa] @copy_addr_take_assign : +// CHECK: [[LD:%.*]] = load [take] %0 +// CHECK-NEXT: [[C:%.*]] = copy_value [[LD]] +// CHECK-NEXT: store [[LD]] to [assign] %1 +// CHECK: return [[C]] +// CHECK-LABEL: } // end sil function 'copy_addr_take_assign' +sil [ossa] @copy_addr_take_assign : $@convention(thin) (@in B, @inout B) -> @owned B { +bb0(%0 : $*B, %1 : $*B): + copy_addr [take] %0 to %1 + %3 = load [copy] %1 + return %3 +} + +// CHECK-LABEL: sil [ossa] @copy_addr_load_take : +// CHECK: [[LD:%.*]] = load [copy] +// CHECK-NOT: copy_addr +// CHECK: return [[LD]] +// CHECK-LABEL: } // end sil function 'copy_addr_load_take' +sil [ossa] @copy_addr_load_take : $@convention(thin) (@in_guaranteed B) -> @owned B { +bb0(%0 : $*B): + %1 = alloc_stack $B + copy_addr %0 to [init] %1 + %3 = load [take] %1 + dealloc_stack %1 + return %3 +} + +// CHECK-LABEL: sil [ossa] @copy_addr_projection : +// CHECK: [[LD:%.*]] = load [trivial] %0 +// CHECK-NEXT: [[A:%.*]] = struct_extract [[LD]] +// CHECK-NEXT: store [[LD]] to [trivial] %1 +// CHECK: return [[A]] +// CHECK-LABEL: } // end sil function 'copy_addr_projection' +sil [ossa] @copy_addr_projection : $@convention(thin) (@in_guaranteed TwoField, @inout TwoField) -> Int { +bb0(%0 : $*TwoField, %1 : $*TwoField): + copy_addr %0 to %1 + %3 = struct_element_addr %1, #TwoField.a + %4 = load [trivial] %3 + return %4 +} + +// CHECK-LABEL: sil [ossa] @copy_addr_aggregate : +// CHECK: [[A_ADDR:%.*]] = struct_element_addr %2 : $*TwoField, #TwoField.a +// CHECK: [[A:%.*]] = load [trivial] %0 +// CHECK: store [[A]] to [trivial] [[A_ADDR]] +// CHECK: [[B_ADDR:%.*]] = struct_element_addr %2 : $*TwoField, #TwoField.b +// CHECK: [[B:%.*]] = load [trivial] %1 +// CHECK: store [[B]] to [trivial] [[B_ADDR]] +// CHECK: [[R:%.*]] = struct $TwoField ([[A]] : $Int, [[B]] : $Int) +// CHECK: return [[R]] +// CHECK-LABEL: } // end sil function 'copy_addr_aggregate' +sil [ossa] @copy_addr_aggregate : $@convention(thin) (@in_guaranteed Int, @in_guaranteed Int, @inout TwoField) -> TwoField { +bb0(%0 : $*Int, %1 : $*Int, %2 : $*TwoField): + %3 = struct_element_addr %2, #TwoField.a + copy_addr %0 to %3 + %5 = struct_element_addr %2, #TwoField.b + copy_addr %1 to %5 + %4 = load [trivial] %2 + return %4 +} + +// CHECK-LABEL: sil [ossa] @copy_addr_not_loadable : +// CHECK: copy_addr +// CHECK: load +// CHECK-LABEL: } // end sil function 'copy_addr_not_loadable' +sil [ossa] @copy_addr_not_loadable : $@convention(thin) (@in_guaranteed ExistentialIntPair, @inout ExistentialIntPair) -> Int { +bb0(%0 : $*ExistentialIntPair, %1 : $*ExistentialIntPair): + copy_addr %0 to %1 + %3 = struct_element_addr %1, #ExistentialIntPair.x + %4 = load [trivial] %3 + return %4 +} + From 5acccf18027b369cf571dc740f285cd89fbe3a72 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 6 Feb 2025 14:36:50 +0100 Subject: [PATCH 2/8] RedundantLoadElimination: ignore memory effects of begin_access Memory effects of begin_access are only defined to prevent the optimizer moving loads and stores across a begin_access. But those memory effects are not relevant for RedundantLoadElimination --- .../RedundantLoadElimination.swift | 2 +- test/SILOptimizer/redundant_load_elim_ossa.sil | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift index 5848ad65759ad..c11ea39cb8697 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift @@ -500,7 +500,7 @@ private struct InstructionScanner { private mutating func visit(instruction: Instruction) -> ScanResult { switch instruction { - case is FixLifetimeInst, is EndAccessInst, is EndBorrowInst: + case is FixLifetimeInst, is BeginAccessInst, is EndAccessInst, is EndBorrowInst: // Those scope-ending instructions are only irrelevant if the preceding load is not changed. // If it is changed from `load [copy]` -> `load [take]` the memory effects of those scope-ending // instructions prevent that the `load [take]` will illegally mutate memory which is protected diff --git a/test/SILOptimizer/redundant_load_elim_ossa.sil b/test/SILOptimizer/redundant_load_elim_ossa.sil index 4d671cb369bce..e9cfece55ce21 100644 --- a/test/SILOptimizer/redundant_load_elim_ossa.sil +++ b/test/SILOptimizer/redundant_load_elim_ossa.sil @@ -1403,6 +1403,24 @@ bb0: return %5 : $Int32 } +// CHECK-LABEL: sil [ossa] @ignore_begin_access : +// CHECK: return %0 +// CHECK-LABEL: } // end sil function 'ignore_begin_access' +sil [ossa] @ignore_begin_access : $@convention(thin) (Int, Int, @inout TwoField) -> Int { +bb0(%0 : $Int, %1 : $Int, %2 : $*TwoField): + %3 = begin_access [modify] [static] %2 + %4 = struct_element_addr %3, #TwoField.a + store %0 to [trivial] %4 + end_access %3 + %7 = begin_access [modify] [static] %2 + %8 = struct_element_addr %7, #TwoField.b + store %0 to [trivial] %8 + end_access %7 + %11 = struct_element_addr %2, #TwoField.a + %12 = load [trivial] %11 + return %12 +} + // CHECK: sil @tbaa_class_alias_nonclass // CHECK: strong_retain [[RET:%[0-9]+]] // CHECK: strong_retain [[RET]] From 2af24703abfad71c0f0f8a386ce8842a9490a821 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 6 Feb 2025 14:51:53 +0100 Subject: [PATCH 3/8] RedundantLoadElimination: insert mark_dependence instructions for a removed load If the memory location depends on something, insert a dependency for the loaded value: ``` %2 = mark_dependence %1 on %0 %3 = load %2 ``` -> ``` %2 = mark_dependence %1 on %0 // not needed anymore, can be removed eventually %3 = load %2 %4 = mark_dependence %3 on %0 // replace %3 with %4 ``` --- .../RedundantLoadElimination.swift | 37 +++++++++++++++++++ .../SILOptimizer/redundant_load_elim_ossa.sil | 33 +++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift index c11ea39cb8697..c8f982829a104 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift @@ -279,6 +279,10 @@ private func replace(load: LoadInst, with availableValues: [AvailableValue], _ c // newValue = ssaUpdater.getValue(inMiddleOf: load.parentBlock) } + + // Make sure to keep dependencies valid after replacing the load + insertMarkDependencies(for: load, context) + load.replace(with: newValue, context) } @@ -306,6 +310,39 @@ private func provideValue( } } +/// If the memory location depends on something, insert a dependency for the loaded value: +/// +/// %2 = mark_dependence %1 on %0 +/// %3 = load %2 +/// -> +/// %2 = mark_dependence %1 on %0 // not needed anymore, can be removed eventually +/// %3 = load %2 +/// %4 = mark_dependence %3 on %0 +/// // replace %3 with %4 +/// +private func insertMarkDependencies(for load: LoadInst, _ context: FunctionPassContext) { + var inserter = MarkDependenceInserter(load: load, context: context) + _ = inserter.walkUp(address: load.address, path: UnusedWalkingPath()) +} + +private struct MarkDependenceInserter : AddressUseDefWalker { + let load: LoadInst + let context: FunctionPassContext + + mutating func walkUp(address: Value, path: UnusedWalkingPath) -> WalkResult { + if let mdi = address as? MarkDependenceInst { + let builder = Builder(after: load, context) + let newMdi = builder.createMarkDependence(value: load, base: mdi.base, kind: mdi.dependenceKind) + load.uses.ignore(user: newMdi).replaceAll(with: newMdi, context) + } + return walkUpDefault(address: address, path: path) + } + + mutating func rootDef(address: Value, path: UnusedWalkingPath) -> WalkResult { + return .continueWalk + } +} + /// In case of a `load [take]` shrink lifetime of the value in memory back to the `availableValue` /// and return the (possibly projected) available value. For example: /// diff --git a/test/SILOptimizer/redundant_load_elim_ossa.sil b/test/SILOptimizer/redundant_load_elim_ossa.sil index e9cfece55ce21..008fe6d079d2c 100644 --- a/test/SILOptimizer/redundant_load_elim_ossa.sil +++ b/test/SILOptimizer/redundant_load_elim_ossa.sil @@ -1271,6 +1271,39 @@ bb0(%0 : $*Builtin.Int64, %1 : @guaranteed $Builtin.NativeObject): return %6 : $(Builtin.Int64, Builtin.Int64) } +// CHECK-LABEL: sil [ossa] @insert_mark_dependence : +// CHECK: mark_dependence +// CHECK: [[MD:%.*]] = mark_dependence %0 : $B on %1 +// CHECK: return [[MD]] +// CHECK-LABEL: } // end sil function 'insert_mark_dependence' +sil [ossa] @insert_mark_dependence : $@convention(thin) (@owned B, @guaranteed B) -> @owned B { +bb0(%0 : @owned $B, %1 : @guaranteed $B): + %2 = alloc_stack $B + store %0 to [init] %2 + %4 = mark_dependence %2 on %1 + %5 = load [take] %4 + dealloc_stack %2 + return %5 +} + +// CHECK-LABEL: sil [ossa] @insert_two_mark_dependences : +// CHECK: mark_dependence +// CHECK: mark_dependence +// CHECK: [[MD1:%.*]] = mark_dependence %0 : $B on %1 +// CHECK: [[MD2:%.*]] = mark_dependence [[MD1]] : $B on %2 +// CHECK: return [[MD2]] +// CHECK-LABEL: } // end sil function 'insert_two_mark_dependences' +sil [ossa] @insert_two_mark_dependences : $@convention(thin) (@owned B, @guaranteed B, @guaranteed B) -> @owned B { +bb0(%0 : @owned $B, %1 : @guaranteed $B, %2 : @guaranteed $B): + %3 = alloc_stack $B + store %0 to [init] %3 + %5 = mark_dependence %3 on %1 + %6 = mark_dependence %5 on %2 + %7 = load [take] %6 + dealloc_stack %3 + return %7 +} + // CHECK-LABEL: sil [ossa] @dont_crash_on_index_addr_projection : // CHECK-LABEL: } // end sil function 'dont_crash_on_index_addr_projection' sil [ossa] @dont_crash_on_index_addr_projection : $@convention(thin) (Builtin.RawPointer) -> (Int, Int, Int, Int) { From ce73deebc48971ecbcd5e7f6d4f68f0994b40166 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 6 Feb 2025 15:10:28 +0100 Subject: [PATCH 4/8] RedundantLoadElimination: add a "mandatory" redundant load elimination pass And make `eliminateRedundantLoads` callable from other optimizations --- .../RedundantLoadElimination.swift | 82 +++++-- .../PassManager/PassRegistration.swift | 1 + .../Optimizer/Utilities/OptUtils.swift | 10 +- .../swift/SILOptimizer/PassManager/Passes.def | 2 + ....sil => mandatory-redundant-load-elim.sil} | 223 ++++++------------ 5 files changed, 141 insertions(+), 177 deletions(-) rename test/SILOptimizer/{predictable_memaccess_opts.sil => mandatory-redundant-load-elim.sil} (89%) diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift index c8f982829a104..902543bbe7830 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift @@ -63,21 +63,33 @@ import SIL /// let redundantLoadElimination = FunctionPass(name: "redundant-load-elimination") { (function: Function, context: FunctionPassContext) in - eliminateRedundantLoads(in: function, ignoreArrays: false, context) + _ = eliminateRedundantLoads(in: function, variant: .regular, context) } // Early RLE does not touch loads from Arrays. This is important because later array optimizations, // like ABCOpt, get confused if an array load in a loop is converted to a pattern with a phi argument. let earlyRedundantLoadElimination = FunctionPass(name: "early-redundant-load-elimination") { (function: Function, context: FunctionPassContext) in - eliminateRedundantLoads(in: function, ignoreArrays: true, context) + _ = eliminateRedundantLoads(in: function, variant: .early, context) } -private func eliminateRedundantLoads(in function: Function, ignoreArrays: Bool, _ context: FunctionPassContext) { +let mandatoryRedundantLoadElimination = FunctionPass(name: "mandatory-redundant-load-elimination") { + (function: Function, context: FunctionPassContext) in + _ = eliminateRedundantLoads(in: function, variant: .mandatory, context) +} + +enum RedundantLoadEliminationVariant { + case mandatory, mandatoryInGlobalInit, early, regular +} +func eliminateRedundantLoads(in function: Function, + variant: RedundantLoadEliminationVariant, + _ context: FunctionPassContext) -> Bool +{ // Avoid quadratic complexity by limiting the number of visited instructions. // This limit is sufficient for most "real-world" functions, by far. var complexityBudget = 50_000 + var changed = false for block in function.blocks.reversed() { @@ -89,50 +101,76 @@ private func eliminateRedundantLoads(in function: Function, ignoreArrays: Bool, if let load = inst as? LoadInst { if !context.continueWithNextSubpassRun(for: load) { - return + return changed } - if ignoreArrays, - let nominal = load.type.nominal, - nominal == context.swiftArrayDecl - { - continue + if complexityBudget < 20 { + complexityBudget = 20 } - // Check if the type can be expanded without a significant increase to - // code size. - // We block redundant load elimination because it might increase - // register pressure for large values. Furthermore, this pass also - // splits values into its projections (e.g - // shrinkMemoryLifetimeAndSplit). - if !load.type.shouldExpand(context) { - continue + if !load.isEligibleForElimination(in: variant, context) { + continue; } - tryEliminate(load: load, complexityBudget: &complexityBudget, context) + changed = tryEliminate(load: load, complexityBudget: &complexityBudget, context) || changed } } } + return changed } -private func tryEliminate(load: LoadInst, complexityBudget: inout Int, _ context: FunctionPassContext) { +private func tryEliminate(load: LoadInst, complexityBudget: inout Int, _ context: FunctionPassContext) -> Bool { switch load.isRedundant(complexityBudget: &complexityBudget, context) { case .notRedundant: - break + return false case .redundant(let availableValues): replace(load: load, with: availableValues, context) + return true case .maybePartiallyRedundant(let subPath): // Check if the a partial load would really be redundant to avoid unnecessary splitting. switch load.isRedundant(at: subPath, complexityBudget: &complexityBudget, context) { case .notRedundant, .maybePartiallyRedundant: - break + return false case .redundant: // The new individual loads are inserted right before the current load and // will be optimized in the following loop iterations. - load.trySplit(context) + return load.trySplit(context) } } } private extension LoadInst { + func isEligibleForElimination(in variant: RedundantLoadEliminationVariant, _ context: FunctionPassContext) -> Bool { + switch variant { + case .mandatory, .mandatoryInGlobalInit: + if loadOwnership == .take { + // load [take] would require to shrinkMemoryLifetime. But we don't want to do this in the mandatory + // pipeline to not shrink or remove an alloc_stack which is relevant for debug info. + return false + } + switch address.accessBase { + case .box, .stack: + break + default: + return false + } + case .early: + // See the comment of `earlyRedundantLoadElimination`. + if let nominal = self.type.nominal, nominal == context.swiftArrayDecl { + return false + } + case .regular: + break + } + // Check if the type can be expanded without a significant increase to code size. + // We block redundant load elimination because it might increase register pressure for large values. + // Furthermore, this pass also splits values into its projections (e.g shrinkMemoryLifetimeAndSplit). + // But: it is required to remove loads, even of large structs, in global init functions to ensure + // that globals (containing large structs) can be statically initialized. + if variant != .mandatoryInGlobalInit, !self.type.shouldExpand(context) { + return false + } + return true + } + enum DataflowResult { case notRedundant case redundant([AvailableValue]) diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift index 506b8e56b3d21..c300261c71129 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift @@ -91,6 +91,7 @@ private func registerSwiftPasses() { registerPass(stripObjectHeadersPass, { stripObjectHeadersPass.run($0) }) registerPass(deadStoreElimination, { deadStoreElimination.run($0) }) registerPass(redundantLoadElimination, { redundantLoadElimination.run($0) }) + registerPass(mandatoryRedundantLoadElimination, { mandatoryRedundantLoadElimination.run($0) }) registerPass(earlyRedundantLoadElimination, { earlyRedundantLoadElimination.run($0) }) registerPass(deinitDevirtualizer, { deinitDevirtualizer.run($0) }) registerPass(lifetimeDependenceDiagnosticsPass, { lifetimeDependenceDiagnosticsPass.run($0) }) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index df10e274b3f70..ec8c65334f5da 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -513,15 +513,16 @@ extension StoreInst { } extension LoadInst { - func trySplit(_ context: FunctionPassContext) { + @discardableResult + func trySplit(_ context: FunctionPassContext) -> Bool { var elements = [Value]() let builder = Builder(before: self, context) if type.isStruct { if (type.nominal as! StructDecl).hasUnreferenceableStorage { - return + return false } guard let fields = type.getNominalFields(in: parentFunction) else { - return + return false } for idx in 0.. LoadOwnership { diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 5ff791ae197ad..0171ffe6714d3 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -252,6 +252,8 @@ PASS(ARCSequenceOpts, "arc-sequence-opts", "ARC Sequence Optimization") PASS(ARCLoopOpts, "arc-loop-opts", "ARC Loop Optimization") +SWIFT_FUNCTION_PASS(MandatoryRedundantLoadElimination, "mandatory-redundant-load-elimination", + "Mandatory Redundant Load Elimination") SWIFT_FUNCTION_PASS(EarlyRedundantLoadElimination, "early-redundant-load-elimination", "Early Redundant Load Elimination") SWIFT_FUNCTION_PASS(RedundantLoadElimination, "redundant-load-elimination", diff --git a/test/SILOptimizer/predictable_memaccess_opts.sil b/test/SILOptimizer/mandatory-redundant-load-elim.sil similarity index 89% rename from test/SILOptimizer/predictable_memaccess_opts.sil rename to test/SILOptimizer/mandatory-redundant-load-elim.sil index 68e55b4f05d96..427b07b62b16c 100644 --- a/test/SILOptimizer/predictable_memaccess_opts.sil +++ b/test/SILOptimizer/mandatory-redundant-load-elim.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all -predictable-memaccess-opts %s | %FileCheck %s +// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all -mandatory-redundant-load-elimination %s | %FileCheck %s sil_stage raw @@ -60,11 +60,9 @@ bb0(%0 : $Builtin.Int32): // CHECK: [[STACK:%.*]] = alloc_stack $Builtin.NativeObject // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] -// CHECK: [[ARG_COPY_2:%.*]] = copy_value [[ARG_COPY]] -// CHECK: destroy_value [[ARG_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[ARG_COPY_2]] +// CHECK: return [[ARG_COPY]] // CHECK: } // end sil function 'simple_nontrivial_load_promotion' sil [ossa] @simple_nontrivial_load_promotion : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject): @@ -85,14 +83,10 @@ bb0(%0 : @owned $Builtin.NativeObject): // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] // CHECK: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] // CHECK: store [[ARG2]] to [init] [[SECOND_ADDR]] -// CHECK: [[ARG1_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY]] -// CHECK: [[ARG2_COPY_BORROW:%.*]] = begin_borrow [[ARG2_COPY]] -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[ARG2_COPY_BORROW:%.*]] : $Builtin.NativeObject) -// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] -// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[ARG2_COPY:%.*]] : $Builtin.NativeObject) // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT_COPY_2]] +// CHECK: return [[RESULT]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion' sil [ossa] @struct_nontrivial_load_promotion : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -150,11 +144,9 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): // CHECK: br bb3([[ARG_COPY]] : // // CHECK: bb3([[RESULT:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] -// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT_COPY_2]] +// CHECK: return [[RESULT]] // CHECK: } // end sil function 'simple_nontrivial_load_promotion_multi_insertpt' sil [ossa] @simple_nontrivial_load_promotion_multi_insertpt : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject): @@ -188,7 +180,7 @@ bb3: // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] // CHECK: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] // CHECK: store [[ARG2]] to [init] [[SECOND_ADDR]] -// CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject, [[ARG2_COPY]] : $Builtin.NativeObject) +// CHECK: br bb3([[ARG2_COPY]] : $Builtin.NativeObject, [[ARG1_COPY]] : $Builtin.NativeObject) // // CHECK: bb2: // CHECK: [[FIRST_ADDR:%.*]] = struct_element_addr [[STACK]] @@ -197,19 +189,13 @@ bb3: // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] // CHECK: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] // CHECK: store [[ARG2]] to [init] [[SECOND_ADDR]] -// CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject, [[ARG2_COPY]] : $Builtin.NativeObject) +// CHECK: br bb3([[ARG2_COPY]] : $Builtin.NativeObject, [[ARG1_COPY]] : $Builtin.NativeObject) // -// CHECK: bb3([[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject, [[ARG2_COPY:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[ARG1_COPY_COPY:%.*]] = copy_value [[ARG1_COPY]] -// CHECK: [[ARG2_COPY_COPY:%.*]] = copy_value [[ARG2_COPY]] -// CHECK: [[ARG1_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY_COPY]] -// CHECK: [[ARG2_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG2_COPY_COPY]] -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[ARG2_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject) -// CHECK: [[RESULT_COPY:%.*]] = copy_value [[RESULT]] -// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY]] +// CHECK: bb3([[ARG2_COPY:%.*]] : @owned $Builtin.NativeObject, [[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject): +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY]] : $Builtin.NativeObject, [[ARG2_COPY]] : $Builtin.NativeObject) // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT_COPY_2]] +// CHECK: return [[RESULT]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion_multi_insertpt' sil [ossa] @struct_nontrivial_load_promotion_multi_insertpt : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -249,7 +235,7 @@ bb3: // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] // CHECK: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] // CHECK: store [[ARG2]] to [init] [[SECOND_ADDR]] -// CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject, [[ARG2_COPY]] : $Builtin.NativeObject) +// CHECK: br bb3([[ARG2_COPY]] : $Builtin.NativeObject, [[ARG1_COPY]] : $Builtin.NativeObject) // // CHECK: bb2: // CHECK: [[FIRST_ADDR:%.*]] = tuple_element_addr [[STACK]] @@ -258,9 +244,9 @@ bb3: // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] // CHECK: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] // CHECK: store [[ARG2]] to [init] [[SECOND_ADDR]] -// CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject, [[ARG2_COPY]] : $Builtin.NativeObject) +// CHECK: br bb3([[ARG2_COPY]] : $Builtin.NativeObject, [[ARG1_COPY]] : $Builtin.NativeObject) // -// CHECK: bb3([[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject, [[ARG2_COPY:%.*]] : @owned $Builtin.NativeObject): +// CHECK: bb3([[ARG2_COPY:%.*]] : @owned $Builtin.NativeObject, [[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject): // CHECK: [[RESULT:%.*]] = tuple ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[ARG2_COPY:%.*]] : $Builtin.NativeObject) // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] @@ -306,9 +292,10 @@ bb3: // CHECK: [[SECOND_ADDR:%.*]] = struct_element_addr [[STACK]] // CHECK: [[ARG1_COPY:%.*]] = copy_value [[ARG1]] // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] +// CHECK: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] // CHECK: store [[ARG2]] to [init] [[SECOND_ADDR]] // CHECK: destroy_value [[ARG3]] -// CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject) +// CHECK: br bb3([[ARG2_COPY]] : $Builtin.NativeObject, [[ARG1_COPY]] : $Builtin.NativeObject) // // CHECK: bb2: // CHECK: [[FIRST_ADDR:%.*]] = struct_element_addr [[STACK]] @@ -316,21 +303,15 @@ bb3: // CHECK: [[ARG1_COPY:%.*]] = copy_value [[ARG1]] // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] // CHECK: destroy_value [[ARG2]] +// CHECK: [[ARG2_COPY:%.*]] = copy_value [[ARG3]] // CHECK: store [[ARG3]] to [init] [[SECOND_ADDR]] -// CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject) +// CHECK: br bb3([[ARG2_COPY]] : $Builtin.NativeObject, [[ARG1_COPY]] : $Builtin.NativeObject) // -// CHECK: bb3([[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[ARG1_COPY_COPY:%.*]] = copy_value [[ARG1_COPY]] -// CHECK: [[SECOND_ADDR:%.*]] = struct_element_addr [[STACK]] -// CHECK: [[SECOND_VAL_COPY:%.*]] = load [copy] [[SECOND_ADDR]] -// CHECK: [[ARG1_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY_COPY]] -// CHECK: [[SECOND_VAL_COPY_BORROW:%.*]] = begin_borrow [[SECOND_VAL_COPY]] -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[SECOND_VAL_COPY_BORROW]] : $Builtin.NativeObject) -// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] -// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] +// CHECK: bb3([[SECOND_VAL_COPY:%.*]] : @owned $Builtin.NativeObject, [[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject): +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[SECOND_VAL_COPY]] : $Builtin.NativeObject) // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT_COPY_2]] +// CHECK: return [[RESULT]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion_multi_insertpt_value_not_fully_available' sil [ossa] @struct_nontrivial_load_promotion_multi_insertpt_value_not_fully_available : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %arg2 : @owned $Builtin.NativeObject): @@ -364,7 +345,6 @@ bb3: // CHECK: bb0([[ARG1:%.*]] : @owned $Builtin.NativeObject, [[ARG2:%.*]] : @owned $Builtin.NativeObject, [[ARG3:%.*]] : @owned $Builtin.NativeObject): // CHECK: [[STACK:%.*]] = alloc_stack $(Builtin.NativeObject, Builtin.NativeObject) // This is here b/c we scalarize loads in our use list. Really, PMO shouldn't scalarize. -// CHECK: [[SCALARIZED_TUPLE_GEP:%.*]] = tuple_element_addr [[STACK]] // CHECK: cond_br undef, bb1, bb2 // // CHECK: bb1: @@ -372,9 +352,10 @@ bb3: // CHECK: [[SECOND_ADDR:%.*]] = tuple_element_addr [[STACK]] // CHECK: [[ARG1_COPY:%.*]] = copy_value [[ARG1]] // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] +// CHECK: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] // CHECK: store [[ARG2]] to [init] [[SECOND_ADDR]] // CHECK: destroy_value [[ARG3]] -// CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject) +// CHECK: br bb3([[ARG2_COPY]] : $Builtin.NativeObject, [[ARG1_COPY]] : $Builtin.NativeObject) // // CHECK: bb2: // CHECK: [[FIRST_ADDR:%.*]] = tuple_element_addr [[STACK]] @@ -382,11 +363,11 @@ bb3: // CHECK: [[ARG1_COPY:%.*]] = copy_value [[ARG1]] // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] // CHECK: destroy_value [[ARG2]] +// CHECK: [[ARG3_COPY:%.*]] = copy_value [[ARG3]] // CHECK: store [[ARG3]] to [init] [[SECOND_ADDR]] -// CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject) +// CHECK: br bb3([[ARG3_COPY]] : $Builtin.NativeObject, [[ARG1_COPY]] : $Builtin.NativeObject) // -// CHECK: bb3([[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[SECOND_VAL_COPY:%.*]] = load [copy] [[SCALARIZED_TUPLE_GEP]] +// CHECK: bb3([[SECOND_VAL_COPY:%.*]] : @owned $Builtin.NativeObject, [[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject): // CHECK: [[RESULT:%.*]] = tuple ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[SECOND_VAL_COPY]] : $Builtin.NativeObject) // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] @@ -432,11 +413,9 @@ bb3: // CHECK: [[COPIED_ARG_FIELD:%.*]] = copy_value [[BORROWED_ARG_FIELD]] // CHECK: end_borrow [[BORROWED_ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] -// CHECK: [[COPIED_ARG_FIELD_COPY_1:%.*]] = copy_value [[COPIED_ARG_FIELD]] -// CHECK: [[COPIED_ARG_FIELD_COPY_2:%.*]] = copy_value [[COPIED_ARG_FIELD_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[COPIED_ARG_FIELD_COPY_2]] +// CHECK: return [[COPIED_ARG_FIELD]] // CHECK: } // end sil function 'simple_partialstructuse_load_promotion' sil [ossa] @simple_partialstructuse_load_promotion : $@convention(thin) (@owned NativeObjectPair) -> (@owned Builtin.NativeObject) { bb0(%0 : @owned $NativeObjectPair): @@ -458,11 +437,9 @@ bb0(%0 : @owned $NativeObjectPair): // CHECK: [[COPIED_ARG_FIELD:%.*]] = copy_value [[BORROWED_ARG_FIELD_2]] // CHECK: end_borrow [[BORROWED_ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] -// CHECK: [[COPIED_ARG_FIELD_COPY_1:%.*]] = copy_value [[COPIED_ARG_FIELD]] -// CHECK: [[COPIED_ARG_FIELD_COPY_2:%.*]] = copy_value [[COPIED_ARG_FIELD_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[COPIED_ARG_FIELD_COPY_2]] +// CHECK: return [[COPIED_ARG_FIELD]] // CHECK: } // end sil function 'simple_partialtupleuse_load_promotion' sil [ossa] @simple_partialtupleuse_load_promotion : $@convention(thin) (@owned NativeObjectAndTuple) -> (@owned Builtin.NativeObject) { bb0(%0 : @owned $NativeObjectAndTuple): @@ -482,10 +459,9 @@ bb0(%0 : @owned $NativeObjectAndTuple): // CHECK: store [[ARG0]] to [init] [[STACK]] // CHECK: [[ARG1_COPY:%.*]] = copy_value [[ARG1]] // CHECK: store [[ARG1]] to [assign] [[STACK]] -// CHECK: [[ARG1_COPY_1:%.*]] = copy_value [[ARG1_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[ARG1_COPY_1]] +// CHECK: return [[ARG1_COPY]] // CHECK: } // end sil function 'simple_assignstore' sil [ossa] @simple_assignstore : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -501,26 +477,18 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): // CHECK-LABEL: sil [ossa] @diamond_test_2 : $@convention(thin) (@owned NativeObjectPair) -> @owned Builtin.NativeObject { // CHECK: bb0([[ARG:%.*]] : @owned $NativeObjectPair): // CHECK: [[STACK:%.*]] = alloc_stack $NativeObjectPair -// CHECK: [[BORROWED_ARG:%.*]] = begin_borrow [[ARG]] -// CHECK: [[LHS1:%.*]] = struct_extract [[BORROWED_ARG]] : $NativeObjectPair, #NativeObjectPair.x -// CHECK: [[LHS1_COPY:%.*]] = copy_value [[LHS1]] -// CHECK: [[BORROWED_ARG:%.*]] = begin_borrow [[ARG]] -// CHECK: [[LHS2:%.*]] = struct_extract [[BORROWED_ARG]] : $NativeObjectPair, #NativeObjectPair.x -// CHECK: [[LHS2_COPY:%.*]] = copy_value [[LHS2]] // CHECK: store [[ARG]] to [init] [[STACK]] // CHECK: cond_br undef, bb1, bb2 // // CHECK: bb1: -// CHECK: destroy_value [[LHS1_COPY]] -// CHECK: [[LHS2_COPY_1:%.*]] = copy_value [[LHS2_COPY]] -// CHECK: [[LHS2_COPY_2:%.*]] = copy_value [[LHS2_COPY_1]] -// CHECK: br bb3([[LHS2_COPY_2]] : +// CHECK: [[A1:%.*]] = struct_element_addr [[STACK]] : $*NativeObjectPair, #NativeObjectPair.x +// CHECK: [[LD1:%.*]] = load [copy] [[A1]] +// CHECK: br bb3([[LD1]] : // // CHECK: bb2: -// CHECK: destroy_value [[LHS2_COPY]] -// CHECK: [[LHS1_COPY_1:%.*]] = copy_value [[LHS1_COPY]] -// CHECK: [[LHS1_COPY_2:%.*]] = copy_value [[LHS1_COPY_1]] -// CHECK: br bb3([[LHS1_COPY_2]] : +// CHECK: [[A2:%.*]] = struct_element_addr [[STACK]] : $*NativeObjectPair, #NativeObjectPair.x +// CHECK: [[LD2:%.*]] = load [copy] [[A2]] +// CHECK: br bb3([[LD2]] : // // CHECK: bb3([[PHI:%.*]] : // CHECK: destroy_addr [[STACK]] @@ -574,40 +542,9 @@ bb0(%0 : @owned $Builtin.NativeObject): // Load Borrow Tests // /////////////////////// +// load_borrow is currently not optimized // CHECK-LABEL: sil [ossa] @load_borrow_promotion : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { // CHECK: bb0([[ARG:%.*]] : -// Block where we have our store and do our lifetime extending copy_value. -// CHECK: [[STACK:%.*]] = alloc_stack $Builtin.NativeObject -// CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] -// CHECK: store [[ARG]] to [init] [[STACK]] -// CHECK: br bb1 -// -// Our load block. Here, we insert our copy_value + begin_borrow that is -// associated with the load_borrow. We can not use the original copy since even -// though in this situation we know that our copy/borrow would be strongly -// control equivalent, this is not always true. To simplify the algorithm, we -// always insert the copy here. We insert a destroy_value to end the lifetime of -// ARG_COPY since we do not have a loop here. -// -// CHECK: bb1: -// CHECK: [[CONTROL_EQUIVALENT_ARG_COPY:%.*]] = copy_value [[ARG_COPY]] -// CHECK: [[BORROWED_ARG_COPY:%.*]] = begin_borrow [[CONTROL_EQUIVALENT_ARG_COPY]] -// CHECK: destroy_value [[ARG_COPY]] -// CHECK: br bb2 -// -// The block where the load_borrow is actually used. We destroy the control -// equivalent arg copy here after the end_borrow. -// -// CHECK: bb2: -// CHECK: [[RESULT:%.*]] = copy_value [[BORROWED_ARG_COPY]] -// CHECK: end_borrow [[BORROWED_ARG_COPY]] -// CHECK: destroy_value [[CONTROL_EQUIVALENT_ARG_COPY]] -// CHECK: br bb3 -// -// The block after the load_borrow is ever used. -// CHECK: bb3: -// CHECK: destroy_addr [[STACK]] -// CHECK: return [[RESULT]] // CHECK: } // end sil function 'load_borrow_promotion' sil [ossa] @load_borrow_promotion : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject): @@ -630,8 +567,8 @@ bb3: return %3 : $Builtin.NativeObject } +// load_borrow is currently not optimized // CHECK-LABEL: sil [ossa] @promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'promote_with_loop_1' sil [ossa] @promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { bb0(%0 : @owned $NativeObjectPair): @@ -648,8 +585,8 @@ bb2: br bb2 } +// load_borrow is currently not optimized // CHECK-LABEL: sil [ossa] @load_borrow_loop_promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'load_borrow_loop_promote_with_loop_2' sil [ossa] @load_borrow_loop_promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { bb0(%0 : @owned $NativeObjectPair): @@ -675,8 +612,8 @@ bb4: return %9999 : $() } +// load_borrow is currently not optimized // CHECK-LABEL: sil [ossa] @load_borrow_promote_two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'load_borrow_promote_two_backedge_loop' sil [ossa] @load_borrow_promote_two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): @@ -719,27 +656,9 @@ bb9: return %9999 : $() } +// load_borrow is currently not optimized // CHECK-LABEL: sil [canonical] [ossa] @load_borrow_tuple_scalarize : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { // CHECK: bb0([[ARG0:%.*]] : @owned ${{.*}}, [[ARG1:%.*]] : -// CHECK: [[TUP:%.*]] = tuple ([[ARG0]] : ${{.*}}, [[ARG1]] : -// CHECK: ([[TUP_0:%.*]], [[TUP_1:%.*]]) = destructure_tuple [[TUP]] -// CHECK: [[TUP_0_COPY:%.*]] = copy_value [[TUP_0]] -// CHECK: [[TUP_1_COPY:%.*]] = copy_value [[TUP_1]] -// CHECK: [[CONTROL_EQUIVALENT_TUP_0_COPY:%.*]] = copy_value [[TUP_0_COPY]] -// CHECK: [[BORROWED_TUP_0_COPY:%.*]] = begin_borrow [[CONTROL_EQUIVALENT_TUP_0_COPY]] -// CHECK: destroy_value [[TUP_0_COPY]] -// CHECK: [[CONTROL_EQUIVALENT_TUP_1_COPY:%.*]] = copy_value [[TUP_1_COPY]] -// CHECK: [[BORROWED_TUP_1_COPY:%.*]] = begin_borrow [[CONTROL_EQUIVALENT_TUP_1_COPY]] -// CHECK: destroy_value [[TUP_1_COPY]] -// CHECK: [[BORROWED_TUP:%.*]] = tuple ([[BORROWED_TUP_0_COPY]] : ${{.*}}, [[BORROWED_TUP_1_COPY]] : -// CHECK: [[TUP_EXT_1:%.*]] = tuple_extract [[BORROWED_TUP]] : -// CHECK: [[TUP_EXT_2:%.*]] = tuple_extract [[BORROWED_TUP]] : -// CHECK: apply {{%.*}}([[TUP_EXT_1]]) -// CHECK: apply {{%.*}}([[TUP_EXT_2]]) -// CHECK: end_borrow [[BORROWED_TUP_0_COPY]] -// CHECK: destroy_value [[CONTROL_EQUIVALENT_TUP_0_COPY]] -// CHECK: end_borrow [[BORROWED_TUP_1_COPY]] -// CHECK: destroy_value [[CONTROL_EQUIVALENT_TUP_1_COPY]] // CHECK: } // end sil function 'load_borrow_tuple_scalarize' sil [canonical] [ossa] @load_borrow_tuple_scalarize : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -806,10 +725,10 @@ bb7: } // CHECK-LABEL: sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial_reload : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { -// CHECK: bb0( -// CHECK-NOT: load [trivial] %{{[0-9][0-9]*}} : $*IntPair -// CHECK-NOT: bb{{[0-9][0-9]*}}( -// CHECK: } // end sil function 'trivial_multiple_available_values_diamond_followed_by_loop_trivial_reload' +// CHECK: bb3([[PHI:%.*]] : $Builtin.Int32): +// CHECK: [[S:%.*]] = struct $IntPair (%0 : $Builtin.Int32, [[PHI]] : $Builtin.Int32) +// CHECK: apply {{%[0-9]+}}([[S]]) +// CHECK: } // end sil function 'trivial_multiple_available_values_diamond_followed_by_loop_trivial_reload' sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial_reload : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { bb0(%0a : $Builtin.Int32, %0b : $Builtin.Int32, %0c : $Builtin.Int32): %func = function_ref @intpair_user : $@convention(thin) (IntPair) -> () @@ -892,9 +811,9 @@ bb7: return %9999 : $() } +// load_borrow is currently not optimized // CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { // CHECK: bb0( -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop' sil [ossa] @multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject): @@ -938,8 +857,8 @@ bb7: return %9999 : $() } +// load_borrow is currently not optimized // CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop_reload' sil [ossa] @multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @owned $Builtin.NativeObject): @@ -985,8 +904,8 @@ bb7: return %9999 : $() } +// load_borrow is currently not optimized // CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop_store_in_loop' sil [ossa] @multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): @@ -1035,8 +954,8 @@ bb7: return %9999 : $() } +// load_borrow is currently not optimized // CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadborrow : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'loop_carry_loadborrow' sil [canonical] [ossa] @loop_carry_loadborrow : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): @@ -1078,8 +997,8 @@ bb8: return %9999 : $() } +// load_borrow is currently not optimized // CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadborrow_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'loop_carry_loadborrow_2' sil [canonical] [ossa] @loop_carry_loadborrow_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): @@ -1121,8 +1040,8 @@ bb8: return %9999 : $() } +// load_borrow is currently not optimized // CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadborrow_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'loop_carry_loadborrow_3' sil [canonical] [ossa] @loop_carry_loadborrow_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): @@ -1170,8 +1089,8 @@ bb8: return %9999 : $() } +// load_borrow is currently not optimized // CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadborrow_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'loop_carry_loadborrow_4' sil [canonical] [ossa] @loop_carry_loadborrow_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): @@ -1219,8 +1138,8 @@ bb8: return %9999 : $() } +// load_borrow is currently not optimized // CHECK-LABEL: sil [ossa] @loop_carry_load_borrow_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'loop_carry_load_borrow_phi_not_control_equivalent' sil [ossa] @loop_carry_load_borrow_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%arg : @owned $Builtin.NativeObject): @@ -1285,10 +1204,10 @@ bbEnd: return %9999 : $() } +// load_borrow is currently not optimized // In this case, we will have that we need to separately lifetime extend our phi // node's copy to prevent leaks along the edge skipping the loop. // CHECK-LABEL: sil [ossa] @loop_carry_load_borrow_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'loop_carry_load_borrow_phi_not_control_equivalent_2' sil [ossa] @loop_carry_load_borrow_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%arg : @owned $Builtin.NativeObject): @@ -1370,8 +1289,8 @@ bbEnd: //--- +// load_borrow is currently not optimized // CHECK-LABEL: sil [ossa] @load_copy_promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { -// CHECK-NOT: load_borrow // CHECK: } // end sil function 'load_copy_promote_with_loop_1' sil [ossa] @load_copy_promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { bb0(%0 : @owned $NativeObjectPair): @@ -1389,8 +1308,8 @@ bb2: } // CHECK-LABEL: sil [ossa] @load_copy_loop_promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'load_copy_loop_promote_with_loop_2' +// CHECK: load [copy] +// CHECK: } // end sil function 'load_copy_loop_promote_with_loop_2' sil [ossa] @load_copy_loop_promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { bb0(%0 : @owned $NativeObjectPair): %1 = alloc_stack $NativeObjectPair @@ -1416,8 +1335,8 @@ bb4: } // CHECK-LABEL: sil [ossa] @load_copy_promote_two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'load_copy_promote_two_backedge_loop' +// CHECK: load [copy] +// CHECK: } // end sil function 'load_copy_promote_two_backedge_loop' sil [ossa] @load_copy_promote_two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): %1 = alloc_stack $Builtin.NativeObject @@ -1461,7 +1380,7 @@ bb9: // CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { // CHECK: bb0( -// CHECK-NOT: load [copy] +// CHECK: load [copy] // CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop' sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject): @@ -1506,7 +1425,7 @@ bb7: } // CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] {{%.*}} : $*NativeObjectPair +// CHECK: load [copy] {{%.*}} : $*NativeObjectPair // CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop_reload' sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @owned $Builtin.NativeObject): @@ -1603,8 +1522,8 @@ bb7: } // CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'loop_carry_loadcopy' +// CHECK: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy' sil [canonical] [ossa] @loop_carry_loadcopy : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () @@ -1646,8 +1565,8 @@ bb8: } // CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'loop_carry_loadcopy_2' +// CHECK: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy_2' sil [canonical] [ossa] @loop_carry_loadcopy_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () @@ -1689,8 +1608,8 @@ bb8: } // CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'loop_carry_loadcopy_3' +// CHECK: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy_3' sil [canonical] [ossa] @loop_carry_loadcopy_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): %func = function_ref @nativeobject_tuple_user : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () @@ -1738,8 +1657,8 @@ bb8: } // CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'loop_carry_loadcopy_4' +// CHECK: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy_4' sil [canonical] [ossa] @loop_carry_loadcopy_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () @@ -1787,8 +1706,8 @@ bb8: } // CHECK-LABEL: sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'load_copy_loop_carry_load_copy_phi_not_control_equivalent' +// CHECK: load [copy] +// CHECK: } // end sil function 'load_copy_loop_carry_load_copy_phi_not_control_equivalent' sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%arg : @owned $Builtin.NativeObject): %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () @@ -1855,8 +1774,8 @@ bbEnd: // In this case, we will have that we need to separately lifetime extend our phi // node's copy to prevent leaks along the edge skipping the loop. // CHECK-LABEL: sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'load_copy_loop_carry_load_copy_phi_not_control_equivalent_2' +// CHECK: load [copy] +// CHECK: } // end sil function 'load_copy_loop_carry_load_copy_phi_not_control_equivalent_2' sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%arg : @owned $Builtin.NativeObject): %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () From ba4081ee76eaaacec3a7c0036cd7a72a3505ebe0 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 6 Feb 2025 15:28:53 +0100 Subject: [PATCH 5/8] Optimizer: replace PredictableMemoryAccessOptimizations with MandatoryRedundantLoadElimination in the pass pipeline PredictableMemoryAccessOptimizations has become unmaintainable as-is. RedundantLoadElimination does (almost) the same thing as PredictableMemoryAccessOptimizations. It's not as powerful but good enough because PredictableMemoryAccessOptimizations is actually only needed for promoting integer values for mandatory constant propagation. And most importantly: RedundantLoadElimination does not insert additional copies which was a big problem in PredictableMemoryAccessOptimizations. Fixes rdar://142814676 --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 2 +- test/DebugInfo/conditional-assign.swift | 10 +- ...distributed_actor_default_init_sil_1.swift | 6 +- ...distributed_actor_default_init_sil_2.swift | 2 +- ...distributed_actor_default_init_sil_5.swift | 4 +- ...distributed_actor_default_init_sil_6.swift | 6 +- ...distributed_actor_default_init_sil_7.swift | 6 +- ...distributed_actor_default_init_sil_8.swift | 8 +- test/IRGen/typed_throws_abi.swift | 8 +- test/Interop/C/struct/foreign-reference.swift | 2 +- .../foreign-reference/reference-counted.swift | 2 +- ...struct-parameter-back-to-cxx-execution.cpp | 2 +- test/SILGen/reference_bindings.swift | 10 +- .../access_marker_mandatory.swift | 3 +- .../constant_evaluable_subset_test.swift | 2 +- ...onstant_evaluable_subset_test_arch64.swift | 2 +- test/SILOptimizer/definite_init_actor.swift | 2 +- .../definite_init_failable_initializers.swift | 180 ++++++--------- ...nite_init_failable_initializers_objc.swift | 13 +- .../definite_init_nsmanagedvalue.swift | 2 + .../definite_init_root_class.swift | 18 +- .../definite_init_value_types.swift | 9 +- test/SILOptimizer/let-property-lowering.swift | 4 +- .../mandatory_inlining_devirt.swift | 3 +- .../package-cmo-inlining-pass.swift | 2 +- .../pointer_conversion_objc.swift | 9 +- test/SILOptimizer/pound_assert.swift | 4 +- .../predictable_memopt_dependence.sil | 60 ++--- .../predictable_memopt_locs.swift | 2 - .../predictable_memopt_ownership.sil | 214 +++++------------- 30 files changed, 191 insertions(+), 406 deletions(-) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index ab97266da0d34..842156e52a92a 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -213,7 +213,7 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) { // Promote loads as necessary to ensure we have enough SSA formation to emit // SSA based diagnostics. - P.addPredictableMemoryAccessOptimizations(); + P.addMandatoryRedundantLoadElimination(); // This phase performs optimizations necessary for correct interoperation of // Swift os log APIs with C os_log ABIs. diff --git a/test/DebugInfo/conditional-assign.swift b/test/DebugInfo/conditional-assign.swift index eb764d92914f8..f15c3188d00da 100644 --- a/test/DebugInfo/conditional-assign.swift +++ b/test/DebugInfo/conditional-assign.swift @@ -16,12 +16,10 @@ public class M { // Verify that definite initialization doesn't create a bogus description of // self pointing to the liveness bitvector. - // CHECK: sil @$s4main1MC4fromAcA12WithDelegate_p_tKcfc - // CHECK: bb0 - // CHECK-NEXT: %2 = alloc_stack $Builtin.Int2 - // CHECK-NOT: let - // CHECK-NOT: name - // CHECK: scope + // CHECK-LABEL: sil @$s4main1MC4fromAcA12WithDelegate_p_tKcfc + // CHECK: bb0 + // CHECK-NOT: alloc_stack $Builtin.Int2{{.*}}let + // CHECK: } // end sil function '$s4main1MC4fromAcA12WithDelegate_p_tKcfc' public init(from d: WithDelegate) throws { guard let delegate = d.delegate as? DelegateB else { throw Err.s(0) } diff --git a/test/Distributed/SIL/distributed_actor_default_init_sil_1.swift b/test/Distributed/SIL/distributed_actor_default_init_sil_1.swift index 4841147caf040..a54d98dd28226 100644 --- a/test/Distributed/SIL/distributed_actor_default_init_sil_1.swift +++ b/test/Distributed/SIL/distributed_actor_default_init_sil_1.swift @@ -44,17 +44,17 @@ distributed actor MyDistActor { // CHECK: [[RELOADED_SYS1:%[0-9]+]] = load [[TP_FIELD2]] : $*FakeActorSystem // CHECK: [[SELF_METATYPE:%[0-9]+]] = metatype $@thick MyDistActor.Type // CHECK: [[ASSIGN_ID_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV8assignIDyAA0C7AddressVxm0B00bC0RzAF0G0RtzlF -// CHECK: [[ID:%[0-9]+]] = apply [[ASSIGN_ID_FN]]([[SELF_METATYPE]], [[RELOADED_SYS1]]) +// CHECK: [[ID:%[0-9]+]] = apply [[ASSIGN_ID_FN]]([[SELF_METATYPE]], // *** save identity *** // CHECK: [[ID_FIELD:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.id -// CHECK: store [[ID]] to [[ID_FIELD]] : $*ActorAddress +// CHECK: copy_addr {{.*}} to [init] [[ID_FIELD]] : $*ActorAddress // *** invoke actorReady *** // CHECK: [[TP_FIELD3:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.actorSystem // CHECK: [[RELOADED_SYS2:%[0-9]+]] = load [[TP_FIELD3]] : $*FakeActorSystem // CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx0B00bC0RzAA0C7AddressV2IDRtzlF -// CHECK: = apply [[READY_FN]]([[SELF]], [[RELOADED_SYS2]]) +// CHECK: = apply [[READY_FN]]([[SELF]], // CHECK: } // end sil function '$s14default_deinit11MyDistActorC11system_syncAC015FakeDistributedE7Systems0hE6SystemV_tcfc' diff --git a/test/Distributed/SIL/distributed_actor_default_init_sil_2.swift b/test/Distributed/SIL/distributed_actor_default_init_sil_2.swift index 4712e7bdf97d1..915845ccacf50 100644 --- a/test/Distributed/SIL/distributed_actor_default_init_sil_2.swift +++ b/test/Distributed/SIL/distributed_actor_default_init_sil_2.swift @@ -37,7 +37,7 @@ distributed actor MyDistActor { // CHECK: store [[SYS_PARAM]] to [[SYS_FIELD]] : $*FakeActorSystem // CHECK: [[ID_FIELD:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.id -// CHECK: store {{%[0-9]+}} to [[ID_FIELD]] : $*ActorAddress +// CHECK: copy_addr {{.*}} to [init] [[ID_FIELD]] : $*ActorAddress // CHECK: [[RAW_BOOL:%[0-9]+]] = struct_extract [[COND]] : $Bool, #Bool._value // CHECK: cond_br [[RAW_BOOL]], [[SUCCESS_BB:bb[0-9]+]], [[FAIL_BB:bb[0-9]+]] diff --git a/test/Distributed/SIL/distributed_actor_default_init_sil_5.swift b/test/Distributed/SIL/distributed_actor_default_init_sil_5.swift index 89610fa734025..f4e8d105899fe 100644 --- a/test/Distributed/SIL/distributed_actor_default_init_sil_5.swift +++ b/test/Distributed/SIL/distributed_actor_default_init_sil_5.swift @@ -37,7 +37,7 @@ distributed actor MyDistActor { // CHECK: [[SYS_FIELD:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.actorSystem // CHECK: store [[SYSTEM]] to [[SYS_FIELD]] : $*FakeActorSystem // CHECK: [[ID_FIELD:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.id -// CHECK: store {{.*}} to [[ID_FIELD]] : $*ActorAddress +// CHECK: copy_addr {{.*}} to [init] [[ID_FIELD]] : $*ActorAddress // CHECK: [[RAW_BOOL:%[0-9]+]] = struct_extract [[COND]] : $Bool, #Bool._value // CHECK: cond_br [[RAW_BOOL]], [[TRUE_BB:bb[0-9]+]], [[FALSE_BB:bb[0-9]+]] @@ -51,7 +51,7 @@ distributed actor MyDistActor { // CHECK: [[FALSE_BB]]: // CHECK: br [[JOIN]] -// CHECK: [[JOIN]]: +// CHECK: [[JOIN]]({{.*}} : $Builtin.Int3): // CHECK: cond_br {{%[0-9]+}}, [[PARTIAL_DEINIT:bb[0-9]+]], [[NO_DEINIT:bb[0-9]+]] // CHECK: [[PARTIAL_DEINIT]]: diff --git a/test/Distributed/SIL/distributed_actor_default_init_sil_6.swift b/test/Distributed/SIL/distributed_actor_default_init_sil_6.swift index c2416e2cb4c73..dd9279142e8b7 100644 --- a/test/Distributed/SIL/distributed_actor_default_init_sil_6.swift +++ b/test/Distributed/SIL/distributed_actor_default_init_sil_6.swift @@ -42,17 +42,17 @@ distributed actor MyDistActor { // CHECK: [[RELOADED_SYS1:%[0-9]+]] = load [[TP_FIELD2]] : $*FakeActorSystem // CHECK: [[SELF_METATYPE:%[0-9]+]] = metatype $@thick MyDistActor.Type // CHECK: [[ASSIGN_ID_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV8assignIDyAA0C7AddressVxm0B00bC0RzAF0G0RtzlF -// CHECK: [[ID:%[0-9]+]] = apply [[ASSIGN_ID_FN]]([[SELF_METATYPE]], [[RELOADED_SYS1]]) +// CHECK: [[ID:%[0-9]+]] = apply [[ASSIGN_ID_FN]]([[SELF_METATYPE]], // *** save identity *** // CHECK: [[ID_FIELD:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.id -// CHECK: store [[ID]] to [[ID_FIELD]] : $*ActorAddress +// CHECK: copy_addr {{.*}} to [init] [[ID_FIELD]] : $*ActorAddress // *** invoke actorReady *** // CHECK: [[TP_FIELD3:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.actorSystem // CHECK: [[RELOADED_SYS2:%[0-9]+]] = load [[TP_FIELD3]] : $*FakeActorSystem // CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx0B00bC0RzAA0C7AddressV2IDRtzlF -// CHECK: = apply [[READY_FN]]([[SELF]], [[RELOADED_SYS2]]) +// CHECK: = apply [[READY_FN]]([[SELF]], // CHECK: return [[SELF]] // CHECK: [[ERROR_BB]]([[ERRVAL:%[0-9]+]] : $any Error): diff --git a/test/Distributed/SIL/distributed_actor_default_init_sil_7.swift b/test/Distributed/SIL/distributed_actor_default_init_sil_7.swift index 0845e872f2d0e..72bc3c9d8aac5 100644 --- a/test/Distributed/SIL/distributed_actor_default_init_sil_7.swift +++ b/test/Distributed/SIL/distributed_actor_default_init_sil_7.swift @@ -49,10 +49,10 @@ distributed actor MyDistActor { // CHECK: [[RELOADED_SYS1:%[0-9]+]] = load [[TP_FIELD2]] : $*FakeActorSystem // CHECK: [[SELF_METATYPE:%[0-9]+]] = metatype $@thick MyDistActor.Type // CHECK: [[ASSIGN_ID_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV8assignIDyAA0C7AddressVxm0B00bC0RzAF0G0RtzlF -// CHECK: [[ID:%[0-9]+]] = apply [[ASSIGN_ID_FN]]([[SELF_METATYPE]], [[RELOADED_SYS1]]) +// CHECK: [[ID:%[0-9]+]] = apply [[ASSIGN_ID_FN]]([[SELF_METATYPE]], {{.*}} // *** save identity *** // CHECK: [[ID_FIELD:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.id -// CHECK: store [[ID]] to [[ID_FIELD]] : $*ActorAddress +// CHECK: copy_addr {{.*}} to [init] [[ID_FIELD]] : $*ActorAddress // CHECK-NOT: apply // CHECK: br [[JOIN_PT:bb[0-9]+]] @@ -65,7 +65,7 @@ distributed actor MyDistActor { // CHECK: [[TP_FIELD3:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.actorSystem // CHECK: [[RELOADED_SYS2:%[0-9]+]] = load [[TP_FIELD3]] : $*FakeActorSystem // CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx0B00bC0RzAA0C7AddressV2IDRtzlF -// CHECK: = apply [[READY_FN]]([[SELF]], [[RELOADED_SYS2]]) +// CHECK: = apply [[READY_FN]]([[SELF]], {{.*}} // CHECK: return [[SELF]] : $MyDistActor // CHECK: [[SYSTEM_ERROR_BB]]{{.*}}: diff --git a/test/Distributed/SIL/distributed_actor_default_init_sil_8.swift b/test/Distributed/SIL/distributed_actor_default_init_sil_8.swift index 319ce12a15a8c..8fcf92effcde9 100644 --- a/test/Distributed/SIL/distributed_actor_default_init_sil_8.swift +++ b/test/Distributed/SIL/distributed_actor_default_init_sil_8.swift @@ -51,11 +51,11 @@ distributed actor MyDistActor { // CHECK: [[SELF_METATYPE:%[0-9]+]] = metatype $@thick MyDistActor.Type // CHECK: strong_retain [[RELOADED_SYS1]] : $FakeRoundtripActorSystem // CHECK: [[ASSIGN_ID_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0a9RoundtripC6SystemC8assignIDyAA0C7AddressVxm0B00bC0RzlF -// CHECK: [[ID:%[0-9]+]] = apply [[ASSIGN_ID_FN]]([[SELF_METATYPE]], [[RELOADED_SYS1]]) -// CHECK: strong_release [[RELOADED_SYS1]] : $FakeRoundtripActorSystem +// CHECK: [[ID:%[0-9]+]] = apply [[ASSIGN_ID_FN]]([[SELF_METATYPE]], {{.*}}) +// CHECK: strong_release {{.*}} : $FakeRoundtripActorSystem // *** save identity *** // CHECK: [[ID_FIELD:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.id -// CHECK: store [[ID]] to [[ID_FIELD]] : $*ActorAddress +// CHECK: copy_addr {{.*}} to [init] [[ID_FIELD]] : $*ActorAddress // CHECK-NOT: apply // CHECK: br [[JOIN_PT:bb[0-9]+]] @@ -68,7 +68,7 @@ distributed actor MyDistActor { // CHECK: [[TP_FIELD3:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.actorSystem // CHECK: [[RELOADED_SYS2:%[0-9]+]] = load [[TP_FIELD3]] : $*FakeRoundtripActorSystem // CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0a9RoundtripC6SystemC10actorReadyyyx0B00bC0RzAA0C7AddressV2IDRtzlF -// CHECK: = apply [[READY_FN]]([[SELF]], [[RELOADED_SYS2]]) +// CHECK: = apply [[READY_FN]]([[SELF]], {{.*}}) // CHECK: return [[SELF]] : $MyDistActor // CHECK: [[SYSTEM_ERROR_BB]]([[ERROR_ARG:%[0-9]+]] : $any Error): diff --git a/test/IRGen/typed_throws_abi.swift b/test/IRGen/typed_throws_abi.swift index 3686cbacc4e25..6f2479d39c44e 100644 --- a/test/IRGen/typed_throws_abi.swift +++ b/test/IRGen/typed_throws_abi.swift @@ -2994,7 +2994,7 @@ func callImplAsync_i5(_ impl: ImplAsync, _ b: Bool) async -> (Int, Int, Int, Int // CHECK: [[SUCCESS]]: // CHECK: ret { float, float, i64 } { float 1.000000e+00, float 2.000000e+00, i64 undef } // CHECK: [[FAIL]]: -// CHECK: [[ERROR_RES0:%.*]] = load i64, ptr %.x1._value, align 8 +// CHECK: [[ERROR_RES0:%.*]] = call swiftcc i64 @"$s16typed_throws_abi7OneWordVACycfC"() // CHECK: store ptr inttoptr (i64 1 to ptr), ptr %2, align 8 // CHECK: [[ERROR_RES:%.*]] = insertvalue { float, float, i64 } undef, i64 [[ERROR_RES0]], 2 // CHECK: ret { float, float, i64 } [[ERROR_RES]] @@ -3049,7 +3049,7 @@ func callNonMatching_f0(_ b: Bool) -> (Int, Float, Float) { // CHECK: [[SUCCESS]]: // CHECK: ret { float, i64, float } { float 1.000000e+00, i64 1, float 2.000000e+00 } // CHECK: [[FAIL]]: -// CHECK: [[ERROR_RES0:%.*]] = load i64, ptr %.x1._value, align 8 +// CHECK: [[ERROR_RES0:%.*]] = call swiftcc i64 @"$s16typed_throws_abi7OneWordVACycfC"() // CHECK: store ptr inttoptr (i64 1 to ptr), ptr %2, align 8 // CHECK: [[ERROR_RES:%.*]] = insertvalue { float, i64, float } undef, i64 [[ERROR_RES0]], 1 // CHECK: ret { float, i64, float } [[ERROR_RES]] @@ -3108,7 +3108,7 @@ func callNonMatching_f1(_ b: Bool) -> (Int, Float, Bool, Float) { // CHECK: call i1 (ptr, i1, ...) @llvm.coro.end.async(ptr {{%.*}}, i1 false, ptr @"$s16typed_throws_abi20nonMatching_f0_asyncySf_SftSbYaAA7OneWordVYKF{{.*}}", ptr {{%.*}}, ptr {{%.*}}, float 1.000000e+00, float 2.000000e+00, i64 undef, ptr null) // CHECK: unreachable // CHECK: 18: -// CHECK: [[ERROR_X:%.*]] = load i64, ptr %.x1._value, align 8 +// CHECK: [[ERROR_X:%.*]] = call swiftcc i64 @"$s16typed_throws_abi7OneWordVACycfC"() // CHECK: [[ERROR_RET:%.*]] = insertvalue { float, float, i64 } undef, i64 [[ERROR_X]], 2 // CHECK: [[ERROR_RET0:%.*]] = extractvalue { float, float, i64 } [[ERROR_RET]], 0 // CHECK: [[ERROR_RET1:%.*]] = extractvalue { float, float, i64 } [[ERROR_RET]], 1 @@ -3168,7 +3168,7 @@ func callNonMatching_f0_async(_ b: Bool) async -> (Int, Float, Float) { // CHECK: call i1 (ptr, i1, ...) @llvm.coro.end.async(ptr {{%.*}}, i1 false, ptr @"$s16typed_throws_abi20nonMatching_f1_asyncySf_SbSftSbYaAA7OneWordVYKF{{.*}}", ptr {{%.*}}, ptr {{%.*}}, float 1.000000e+00, i64 1, float 2.000000e+00, ptr null) // CHECK: unreachable // CHECK: [[ERROR]]: -// CHECK: [[ERROR_X:%.*]] = load i64, ptr %.x1._value, align 8 +// CHECK: [[ERROR_X:%.*]] = call swiftcc i64 @"$s16typed_throws_abi7OneWordVACycfC"() // CHECK: [[ERROR_RET:%.*]] = insertvalue { float, i64, float } undef, i64 [[ERROR_X]], 1 // CHECK: [[ERROR_RET0:%.*]] = extractvalue { float, i64, float } [[ERROR_RET]], 0 // CHECK: [[ERROR_RET1:%.*]] = extractvalue { float, i64, float } [[ERROR_RET]], 1 diff --git a/test/Interop/C/struct/foreign-reference.swift b/test/Interop/C/struct/foreign-reference.swift index 2fc29ce177e59..43680cd995f1f 100644 --- a/test/Interop/C/struct/foreign-reference.swift +++ b/test/Interop/C/struct/foreign-reference.swift @@ -16,7 +16,7 @@ public func blackHole(_ _: T) { } ReferenceCountedTestSuite.test("Local") { var x = createLocalCount() #if NO_OPTIMIZATIONS - expectEqual(x.value, 6) // This is 6 because of "var x" "x.value" * 2 and "(x, x, x)". + expectEqual(x.value, 2) #endif let t = (x, x, x) diff --git a/test/Interop/Cxx/foreign-reference/reference-counted.swift b/test/Interop/Cxx/foreign-reference/reference-counted.swift index 0555fa39c9816..a9e63c07b5298 100644 --- a/test/Interop/Cxx/foreign-reference/reference-counted.swift +++ b/test/Interop/Cxx/foreign-reference/reference-counted.swift @@ -20,7 +20,7 @@ public func blackHole(_ _: T) { } func localTest() { var x = NS.LocalCount.create() #if NO_OPTIMIZATIONS - expectEqual(x.value, 8) // This is 8 because of "var x" "x.value" * 2, two method calls on x, and "(x, x, x)". + expectEqual(x.value, 2) #endif expectEqual(x.returns42(), 42) diff --git a/test/Interop/CxxToSwiftToCxx/consuming-cxx-struct-parameter-back-to-cxx-execution.cpp b/test/Interop/CxxToSwiftToCxx/consuming-cxx-struct-parameter-back-to-cxx-execution.cpp index 22e722556a957..d4eaf555bb0c4 100644 --- a/test/Interop/CxxToSwiftToCxx/consuming-cxx-struct-parameter-back-to-cxx-execution.cpp +++ b/test/Interop/CxxToSwiftToCxx/consuming-cxx-struct-parameter-back-to-cxx-execution.cpp @@ -137,8 +137,8 @@ int main() { } // CHECK: create NonTrivialTemplate // CHECK-NEXT: copy NonTrivialTemplate -// CHECK-NEXT: x and y: 1, 2 // CHECK-NEXT: ~NonTrivialTemplate +// CHECK-NEXT: x and y: 1, 2 // CHECK-NEXT: DoneCall // CHECK-NEXT: ~NonTrivialTemplate { diff --git a/test/SILGen/reference_bindings.swift b/test/SILGen/reference_bindings.swift index cc0032ab78b97..c45b51ef27768 100644 --- a/test/SILGen/reference_bindings.swift +++ b/test/SILGen/reference_bindings.swift @@ -35,10 +35,10 @@ func doSomething() {} // SIL: [[BOX:%.*]] = alloc_stack [var_decl] $Int, var, name "x" // SIL: [[INOUT_BOX:%.*]] = alloc_stack [var_decl] $Int, var, name "x2" // SIL: [[ACCESS:%.*]] = begin_access [modify] [static] [[BOX]] -// SIL: store {{%.*}} to [[INOUT_BOX]] +// SIL: copy_addr [take] [[ACCESS]] to [init] [[INOUT_BOX]] // SIL: [[FUNC:%.*]] = function_ref @$s18reference_bindings11doSomethingyyF : $@convention(thin) () -> () // SIL: apply [[FUNC]]() -// SIL: store {{%.*}} to [[ACCESS]] +// SIL: copy_addr [take] [[INOUT_BOX]] to [init] [[ACCESS]] // SIL: end_access [[ACCESS]] // SIL: } // end sil function '$s18reference_bindings13testBindToVaryyF' func testBindToVar() { @@ -64,11 +64,9 @@ func testBindToVar() { // SIL: bb0([[ARG:%.*]] : $*String): // SIL: [[STACK:%.*]] = alloc_stack [var_decl] $String // SIL: [[ACCESS:%.*]] = begin_access [modify] [static] [[ARG]] -// SIL: [[VAL:%.*]] = load [[ACCESS]] -// SIL: store [[VAL]] to [[STACK]] +// SIL: copy_addr [take] [[ACCESS]] to [init] [[STACK]] // SIL: apply {{%.*}} -// SIL: [[VAL:%.*]] = load [[STACK]] -// SIL: store [[VAL]] to [[ACCESS]] +// SIL: copy_addr [take] [[STACK]] to [init] [[ACCESS]] // SIL: end_access [[ACCESS]] // SIL: dealloc_stack [[STACK]] // SIL: } // end sil function '$s18reference_bindings15testBindToInOutyySSzF' diff --git a/test/SILOptimizer/access_marker_mandatory.swift b/test/SILOptimizer/access_marker_mandatory.swift index 9973512af083b..58b28460b44e9 100644 --- a/test/SILOptimizer/access_marker_mandatory.swift +++ b/test/SILOptimizer/access_marker_mandatory.swift @@ -17,9 +17,8 @@ public struct S { // CHECK: [[WRITE:%.*]] = begin_access [modify] [static] [[STK]] : $*S // CHECK: store %{{.*}} to [[WRITE]] : $*S // CHECK: end_access [[WRITE]] -// CHECK: bb3: +// CHECK: bb3([[RET:%.*]] : $S): // CHECK: [[READ:%.*]] = begin_access [read] [static] [[STK]] : $*S -// CHECK: [[RET:%.*]] = load [[READ]] : $*S // CHECK: end_access [[READ]] // CHECK: destroy_addr [[STK]] // CHECK: dealloc_stack [[STK]] diff --git a/test/SILOptimizer/constant_evaluable_subset_test.swift b/test/SILOptimizer/constant_evaluable_subset_test.swift index 43316fb26fce1..27fe463f275fb 100644 --- a/test/SILOptimizer/constant_evaluable_subset_test.swift +++ b/test/SILOptimizer/constant_evaluable_subset_test.swift @@ -14,7 +14,7 @@ // especially performance inlining as it inlines functions such as String.+= // that the evaluator has special knowledge about. // -// RUN: not %target-sil-opt -sil-print-types -opt-mode=speed -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -mandatory-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_silgen.sil > /dev/null 2> %t/error-output-mandatory +// RUN: not %target-sil-opt -sil-print-types -opt-mode=speed -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -mandatory-inlining -mandatory-redundant-load-elimination -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -mandatory-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_silgen.sil > /dev/null 2> %t/error-output-mandatory // // RUN: %FileCheck %S/Inputs/constant_evaluable.swift < %t/error-output-mandatory diff --git a/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift b/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift index 62fca074c400d..bd53c23a60c3c 100644 --- a/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift +++ b/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift @@ -16,7 +16,7 @@ // especially performance inlining as it inlines functions such as String.+= // that the evaluator has special knowledge about. // -// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -mandatory-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_arch64_silgen.sil > /dev/null 2> %t/error-output-mandatory +// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -ownership-model-eliminator -mandatory-inlining -mandatory-redundant-load-elimination -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -mandatory-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_arch64_silgen.sil > /dev/null 2> %t/error-output-mandatory // // RUN: %FileCheck %s < %t/error-output-mandatory diff --git a/test/SILOptimizer/definite_init_actor.swift b/test/SILOptimizer/definite_init_actor.swift index 36904d737cdb2..99a0dd8009f8d 100644 --- a/test/SILOptimizer/definite_init_actor.swift +++ b/test/SILOptimizer/definite_init_actor.swift @@ -188,9 +188,9 @@ actor BoringActor { // CHECK: store [[INIT2]] to [[SELF_ALLOC]] : $*SingleVarActor // CHECK-NEXT: hop_to_executor [[INIT2]] : $SingleVarActor + // CHECK: bb3([[T0:%.*]] : $SingleVarActor): // CHECK: [[ARBITRARY_FN:%.*]] = function_ref @$s4test14arbitraryAsyncyyYaF // CHECK-NEXT: apply [[ARBITRARY_FN]]() - // CHECK-NEXT: [[T0:%.*]] = load [[SELF_ALLOC]] : // CHECK-NEXT: strong_retain [[T0]] // CHECK-NEXT: [[T1:%.*]] = init_existential_ref [[T0]] : // CHECK-NEXT: [[T2:%.*]] = enum $Optional, #Optional.some!enumelt, [[T1]] : diff --git a/test/SILOptimizer/definite_init_failable_initializers.swift b/test/SILOptimizer/definite_init_failable_initializers.swift index d8c329a632fb3..18664db3c27d4 100644 --- a/test/SILOptimizer/definite_init_failable_initializers.swift +++ b/test/SILOptimizer/definite_init_failable_initializers.swift @@ -404,15 +404,16 @@ struct ThrowStruct { // CHECK: [[SELF_BOX:%.*]] = alloc_stack [var_decl] $ThrowStruct // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: // function_ref // CHECK-NEXT: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): +// CHECK-NEXT: [[LD:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: retain_value [[LD]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: return [[NEW_SELF]] +// CHECK-NEXT: return [[LD]] // CHECK: bb2([[ERROR:%.*]] : $any Error): // CHECK: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] @@ -424,32 +425,27 @@ struct ThrowStruct { // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers11ThrowStructV27failDuringOrAfterDelegationACSi_tKcfC // CHECK: bb0(%0 : $Int, %1 : $@thin ThrowStruct.Type): -// CHECK-NEXT: [[BITMAP_BOX:%.*]] = alloc_stack $Builtin.Int1 // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack [dynamic_lifetime] [var_decl] $ThrowStruct // CHECK-NEXT: [[ZERO:%.*]] = integer_literal $Builtin.Int1, 0 -// CHECK-NEXT: store [[ZERO]] to [[BITMAP_BOX]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV4failACyt_tKcfC // CHECK-NEXT: try_apply [[INIT_FN]](%1) // CHECK: bb1([[NEW_SELF:.*]] : $ThrowStruct): // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 -// CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: // function_ref // CHECK-NEXT: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): +// CHECK-NEXT: [[LD:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: retain_value [[LD]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] -// CHECK-NEXT: return [[NEW_SELF]] +// CHECK-NEXT: return [[LD]] // CHECK: bb3([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: br bb5([[ERROR]] : $any Error) +// CHECK-NEXT: br bb5([[ERROR]] : $any Error, [[ZERO]] : $Builtin.Int1) // CHECK: bb4([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: release_value [[NEW_SELF]] -// CHECK-NEXT: br bb5([[ERROR]] : $any Error) -// CHECK: bb5([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: [[COND:%.*]] = load [[BITMAP_BOX]] +// CHECK-NEXT: br bb5([[ERROR]] : $any Error, [[BIT]] : $Builtin.Int1) +// CHECK: bb5([[ERROR:%.*]] : $any Error, [[COND:%.*]] : $Builtin.Int1): // CHECK-NEXT: cond_br [[COND]], bb6, bb7 // CHECK: bb6: // CHECK-NEXT: destroy_addr [[SELF_BOX]] @@ -458,7 +454,6 @@ struct ThrowStruct { // CHECK-NEXT: br bb8 // CHECK: bb8: // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] // CHECK-NEXT: throw [[ERROR]] init(failDuringOrAfterDelegation: Int) throws { try self.init(fail: ()) @@ -467,34 +462,29 @@ struct ThrowStruct { // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers11ThrowStructV27failBeforeOrAfterDelegationACSi_tKcfC // CHECK: bb0(%0 : $Int, %1 : $@thin ThrowStruct.Type): -// CHECK-NEXT: [[BITMAP_BOX:%.*]] = alloc_stack $Builtin.Int1 // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack [dynamic_lifetime] [var_decl] $ThrowStruct // CHECK-NEXT: [[ZERO:%.*]] = integer_literal $Builtin.Int1, 0 -// CHECK-NEXT: store [[ZERO]] to [[BITMAP_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 -// CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: // function_ref // CHECK-NEXT: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): +// CHECK-NEXT: [[LD:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: retain_value [[LD]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] -// CHECK-NEXT: return [[NEW_SELF]] +// CHECK-NEXT: return [[LD]] // CHECK: bb3([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: br bb5([[ERROR]] : $any Error) +// CHECK-NEXT: br bb5([[ERROR]] : $any Error, [[ZERO]] : $Builtin.Int1) // CHECK: bb4([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: release_value [[NEW_SELF]] -// CHECK-NEXT: br bb5([[ERROR]] : $any Error) -// CHECK: bb5([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: [[COND:%.*]] = load [[BITMAP_BOX]] +// CHECK-NEXT: br bb5([[ERROR]] : $any Error, [[BIT]] : $Builtin.Int1) +// CHECK: bb5([[ERROR:%.*]] : $any Error, [[COND:%.*]] : $Builtin.Int1): // CHECK-NEXT: cond_br [[COND]], bb6, bb7 // CHECK: bb6: // CHECK-NEXT: destroy_addr [[SELF_BOX]] @@ -503,7 +493,6 @@ struct ThrowStruct { // CHECK-NEXT: br bb8 // CHECK: bb8: // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] // CHECK-NEXT: throw [[ERROR]] init(failBeforeOrAfterDelegation: Int) throws { try unwrap(failBeforeOrAfterDelegation) @@ -598,17 +587,17 @@ struct ThrowStruct { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [static] [[SELF_BOX]] : $*ThrowStruct -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[WRITE]] // CHECK-NEXT: end_access [[WRITE]] : $*ThrowStruct // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): +// CHECK-NEXT: [[LD:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: retain_value [[LD]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: return [[NEW_SELF]] +// CHECK-NEXT: return [[LD]] // CHECK: bb2([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: release_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: throw [[ERROR]] @@ -848,13 +837,13 @@ class FailableDerivedClass : FailableBaseClass { // CHECK: bb0([[SELF:%.*]] : $FailableDerivedClass): // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack [var_decl] $FailableDerivedClass // CHECK: store [[SELF]] to [[SELF_BOX]] +// CHECK: [[LD:%.*]] = load [[SELF_BOX]] // CHECK: [[CANARY_FUN:%.*]] = function_ref @$s35definite_init_failable_initializers6CanaryCACycfC : // CHECK: [[CANARY:%.*]] = apply [[CANARY_FUN]]( -// CHECK-NEXT: [[MEMBER_ADDR:%.*]] = ref_element_addr [[SELF]] +// CHECK-NEXT: [[MEMBER_ADDR:%.*]] = ref_element_addr [[LD]] // CHECK-NEXT: [[WRITE:%.*]] = begin_access [init] [static] [[MEMBER_ADDR]] : $*Canary // CHECK-NEXT: store [[CANARY]] to [[WRITE]] // CHECK-NEXT: end_access [[WRITE]] : $*Canary -// CHECK-NEXT: strong_release [[SELF]] // CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17FailableBaseClassC28failBeforeFullInitializationACSgyt_tcfc @@ -977,10 +966,8 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers17ThrowDerivedClassC28failBeforeFullInitialization0h6DuringjK0ACSi_SitKcfc // CHECK: bb0(%0 : $Int, %1 : $Int, %2 : $ThrowDerivedClass): -// CHECK-NEXT: [[BITMAP_BOX:%.*]] = alloc_stack $Builtin.Int1 // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack [dynamic_lifetime] [var_decl] $ThrowDerivedClass // CHECK-NEXT: [[ZERO:%.*]] = integer_literal $Builtin.Int1, 0 -// CHECK-NEXT: store [[ZERO]] to [[BITMAP_BOX]] // CHECK: store %2 to [[SELF_BOX]] : $*ThrowDerivedClass // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) @@ -989,7 +976,6 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassCACyKcfc // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 -// CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] // CHECK: try_apply [[INIT_FN]]([[BASE_SELF]]) // CHECK: bb2([[NEW_SELF:%.*]] : $ThrowBaseClass): // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] @@ -997,14 +983,12 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] // CHECK-NEXT: return [[DERIVED_SELF]] // CHECK: bb3([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: br bb5([[ERROR]] : $any Error) +// CHECK-NEXT: br bb5([[ERROR]] : $any Error, [[ZERO]] : $Builtin.Int1) // CHECK: bb4([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: br bb5([[ERROR]] : $any Error) -// CHECK: bb5([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: [[COND:%.*]] = load [[BITMAP_BOX]] +// CHECK-NEXT: br bb5([[ERROR]] : $any Error, [[BIT]] : $Builtin.Int1) +// CHECK: bb5([[ERROR:%.*]] : $any Error, [[COND:%.*]] : $Builtin.Int1): // CHECK-NEXT: cond_br [[COND]], bb6, bb7 // CHECK: bb6: // CHECK-NEXT: br bb8 @@ -1015,7 +999,6 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: br bb8 // CHECK: bb8: // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] // CHECK-NEXT: throw [[ERROR]] init(failBeforeFullInitialization: Int, failDuringFullInitialization: Int) throws { try unwrap(failBeforeFullInitialization) @@ -1031,16 +1014,16 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassC6noFailACyt_tcfc // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]]([[BASE_SELF]]) // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] -// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): +// CHECK-NEXT: [[LD:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: strong_retain [[LD]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: return [[DERIVED_SELF]] +// CHECK-NEXT: return [[LD]] // CHECK: bb2([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: strong_release [[DERIVED_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: throw [[ERROR]] @@ -1051,37 +1034,32 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers17ThrowDerivedClassC27failAfterFullInitialization0h6DuringjK0ACSi_SitKcfc // CHECK: bb0(%0 : $Int, %1 : $Int, %2 : $ThrowDerivedClass): -// CHECK-NEXT: [[BITMAP_BOX:%.*]] = alloc_stack $Builtin.Int2 // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack [dynamic_lifetime] [var_decl] $ThrowDerivedClass -// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int2, 0 -// CHECK-NEXT: store [[ZERO]] to [[BITMAP_BOX]] // CHECK: store %2 to [[SELF_BOX]] // CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[DERIVED_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassCACyKcfc +// CHECK: [[BIT1:%.*]] = integer_literal $Builtin.Int2, 1 // CHECK: try_apply [[INIT_FN]]([[DERIVED_SELF]]) // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowBaseClass): // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int2, -1 -// CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] -// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): +// CHECK-NEXT: [[LD:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: strong_retain [[LD]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] -// CHECK-NEXT: return [[DERIVED_SELF]] +// CHECK-NEXT: return [[LD]] // CHECK: bb3([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: br bb5([[ERROR]] : $any Error) +// CHECK-NEXT: br bb5([[ERROR]] : $any Error, [[BIT1]] : $Builtin.Int2) // CHECK: bb4([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: strong_release [[DERIVED_SELF]] -// CHECK-NEXT: br bb5([[ERROR]] : $any Error) -// CHECK: bb5([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] +// CHECK-NEXT: br bb5([[ERROR]] : $any Error, [[BIT]] : $Builtin.Int2) +// CHECK: bb5([[ERROR:%.*]] : $any Error, [[COND:%.*]] : $Builtin.Int2): // CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Int2, 1 -// CHECK-NEXT: [[BITMAP_MSB:%.*]] = builtin "lshr_Int2"([[BITMAP]] : $Builtin.Int2, [[ONE]] : $Builtin.Int2) +// CHECK-NEXT: [[BITMAP_MSB:%.*]] = builtin "lshr_Int2"([[COND]] : $Builtin.Int2, [[ONE]] : $Builtin.Int2) // CHECK-NEXT: [[COND:%.*]] = builtin "trunc_Int2_Int1"([[BITMAP_MSB]] : $Builtin.Int2) // CHECK-NEXT: cond_br [[COND]], bb6, bb7 // CHECK: bb6: @@ -1091,7 +1069,6 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: br bb8 // CHECK: bb8: // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] // CHECK-NEXT: throw [[ERROR]] init(failAfterFullInitialization: Int, failDuringFullInitialization: Int) throws { try super.init() @@ -1100,43 +1077,35 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers17ThrowDerivedClassC28failBeforeFullInitialization0h5AfterjK0ACSi_SitKcfc // CHECK: bb0(%0 : $Int, %1 : $Int, %2 : $ThrowDerivedClass): -// CHECK-NEXT: [[BITMAP_BOX:%.*]] = alloc_stack $Builtin.Int2 // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack [dynamic_lifetime] [var_decl] $ThrowDerivedClass // CHECK-NEXT: [[ZERO:%.*]] = integer_literal $Builtin.Int2, 0 -// CHECK-NEXT: store [[ZERO]] to [[BITMAP_BOX]] // CHECK: store %2 to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: [[TWO:%.*]] = integer_literal $Builtin.Int2, -2 -// CHECK-NEXT: store [[TWO]] to [[BITMAP_BOX]] // CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassC6noFailACyt_tcfc // CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Int2, -1 -// CHECK-NEXT: store [[ONE]] to [[BITMAP_BOX]] // CHECK: [[NEW_SELF:%.*]] = apply [[INIT_FN]]([[BASE_SELF]]) // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] -// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%1) // CHECK: bb2([[RESULT:%.*]] : $Int): +// CHECK-NEXT: [[LD:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: strong_retain [[LD]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] -// CHECK-NEXT: return [[DERIVED_SELF]] +// CHECK-NEXT: return [[LD]] // CHECK: bb3([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: br bb5([[ERROR]] : $any Error) +// CHECK-NEXT: br bb5([[ERROR]] : $any Error, [[ZERO]] : $Builtin.Int2) // CHECK: bb4([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: strong_release [[DERIVED_SELF]] -// CHECK-NEXT: br bb5([[ERROR]] : $any Error) -// CHECK: bb5([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] +// CHECK-NEXT: br bb5([[ERROR]] : $any Error, [[ONE]] : $Builtin.Int2) +// CHECK: bb5([[ERROR:%.*]] : $any Error, [[BITMAP:%.*]] : $Builtin.Int2): // CHECK-NEXT: [[COND:%.*]] = builtin "trunc_Int2_Int1"([[BITMAP]] : $Builtin.Int2) : $Builtin.Int1 // CHECK-NEXT: cond_br [[COND]], bb6, bb10 // CHECK: bb6: -// CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] // CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Int2, 1 // CHECK-NEXT: [[SHIFTED:%.*]] = builtin "lshr_Int2"([[BITMAP]] : $Builtin.Int2, [[ONE]] : $Builtin.Int2) : $Builtin.Int2 // CHECK-NEXT: [[COND:%.*]] = builtin "trunc_Int2_Int1"([[SHIFTED]] : $Builtin.Int2) : $Builtin.Int1 @@ -1155,7 +1124,6 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: br bb11 // CHECK: bb11: // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] // CHECK-NEXT: throw [[ERROR]] : $any Error init(failBeforeFullInitialization: Int, failAfterFullInitialization: Int) throws { try unwrap(failBeforeFullInitialization) @@ -1165,10 +1133,8 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers17ThrowDerivedClassC28failBeforeFullInitialization0h6DuringjK00h5AfterjK0ACSi_S2itKcfc // CHECK: bb0(%0 : $Int, %1 : $Int, %2 : $Int, %3 : $ThrowDerivedClass): -// CHECK-NEXT: [[BITMAP_BOX:%.*]] = alloc_stack $Builtin.Int2 // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack [dynamic_lifetime] [var_decl] $ThrowDerivedClass // CHECK-NEXT: [[ZERO:%.*]] = integer_literal $Builtin.Int2, 0 -// CHECK-NEXT: store [[ZERO]] to [[BITMAP_BOX]] // CHECK: store %3 to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) @@ -1177,34 +1143,29 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassCACyKcfc // CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Int2, 1 -// CHECK-NEXT: store [[ONE]] to [[BITMAP_BOX]] // CHECK: try_apply [[INIT_FN]]([[BASE_SELF]]) // CHECK: bb2([[NEW_SELF:%.*]] : $ThrowBaseClass): // CHECK-NEXT: [[NEG_ONE:%.*]] = integer_literal $Builtin.Int2, -1 -// CHECK-NEXT: store [[NEG_ONE]] to [[BITMAP_BOX]] // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] -// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%2) // CHECK: bb3([[RESULT:%.*]] : $Int): +// CHECK-NEXT: [[LD:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: strong_retain [[LD]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] -// CHECK-NEXT: return [[DERIVED_SELF]] +// CHECK-NEXT: return [[LD]] // CHECK: bb4([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: br bb7([[ERROR]] : $any Error) +// CHECK-NEXT: br bb7([[ERROR]] : $any Error, [[ZERO]] : $Builtin.Int2) // CHECK: bb5([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: br bb7([[ERROR]] : $any Error) +// CHECK-NEXT: br bb7([[ERROR]] : $any Error, [[ONE]] : $Builtin.Int2) // CHECK: bb6([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: strong_release [[DERIVED_SELF]] -// CHECK-NEXT: br bb7([[ERROR]] : $any Error) -// CHECK: bb7([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] +// CHECK-NEXT: br bb7([[ERROR]] : $any Error, [[NEG_ONE]] : $Builtin.Int2) +// CHECK: bb7([[ERROR:%.*]] : $any Error, [[BITMAP:%.*]] : $Builtin.Int2): // CHECK-NEXT: [[COND:%.*]] = builtin "trunc_Int2_Int1"([[BITMAP]] : $Builtin.Int2) // CHECK-NEXT: cond_br [[COND]], bb8, bb12 // CHECK: bb8: -// CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] // CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Int2, 1 // CHECK-NEXT: [[BITMAP_MSB:%.*]] = builtin "lshr_Int2"([[BITMAP]] : $Builtin.Int2, [[ONE]] : $Builtin.Int2) // CHECK-NEXT: [[COND:%.*]] = builtin "trunc_Int2_Int1"([[BITMAP_MSB]] : $Builtin.Int2) @@ -1223,7 +1184,6 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: br bb13 // CHECK: bb13: // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] // CHECK-NEXT: throw [[ERROR]] init(failBeforeFullInitialization: Int, failDuringFullInitialization: Int, failAfterFullInitialization: Int) throws { try unwrap(failBeforeFullInitialization) @@ -1330,16 +1290,16 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack [var_decl] $ThrowDerivedClass // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassC6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) -// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): +// CHECK-NEXT: [[LD:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: strong_retain [[LD]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: return [[NEW_SELF]] +// CHECK-NEXT: return [[LD]] // CHECK: bb2([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: strong_release [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: throw [[ERROR]] @@ -1350,32 +1310,27 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers17ThrowDerivedClassC27failDuringOrAfterDelegationACSi_tKcfC // CHECK: bb0(%0 : $Int, %1 : $@thick ThrowDerivedClass.Type): -// CHECK: [[BITMAP_BOX:%.*]] = alloc_stack $Builtin.Int1 // CHECK: [[SELF_BOX:%.*]] = alloc_stack [dynamic_lifetime] [var_decl] $ThrowDerivedClass // CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int1, 0 -// CHECK-NEXT: store [[ZERO]] to [[BITMAP_BOX]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassCACyKcfC // CHECK-NEXT: try_apply [[INIT_FN]](%1) // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowDerivedClass): // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 -// CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] -// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): +// CHECK-NEXT: [[LD:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: strong_retain [[LD]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] -// CHECK-NEXT: return [[NEW_SELF]] +// CHECK-NEXT: return [[LD]] // CHECK: bb3([[ERROR1:%.*]] : $any Error): -// CHECK-NEXT: br bb5([[ERROR1]] : $any Error) +// CHECK-NEXT: br bb5([[ERROR1]] : $any Error, [[ZERO]] : $Builtin.Int1) // CHECK: bb4([[ERROR2:%.*]] : $any Error): -// CHECK-NEXT: strong_release [[NEW_SELF]] -// CHECK-NEXT: br bb5([[ERROR2]] : $any Error) -// CHECK: bb5([[ERROR3:%.*]] : $any Error): -// CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] -// CHECK: cond_br {{.*}}, bb6, bb7 +// CHECK-NEXT: br bb5([[ERROR2]] : $any Error, [[BIT]] : $Builtin.Int1) +// CHECK: bb5([[ERROR3:%.*]] : $any Error, [[COND:%.*]] : $Builtin.Int1): +// CHECK: cond_br [[COND]], bb6, bb7 // CHECK: bb6: // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: br bb8 @@ -1383,7 +1338,6 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: br bb8 // CHECK: bb8: // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] // CHECK-NEXT: throw [[ERROR3]] convenience init(failDuringOrAfterDelegation: Int) throws { try self.init() @@ -1392,34 +1346,29 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers17ThrowDerivedClassC27failBeforeOrAfterDelegationACSi_tKcfC // CHECK: bb0(%0 : $Int, %1 : $@thick ThrowDerivedClass.Type): -// CHECK-NEXT: [[BITMAP_BOX:%.*]] = alloc_stack $Builtin.Int1 // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack [dynamic_lifetime] [var_decl] $ThrowDerivedClass // CHECK-NEXT: [[ZERO:%.*]] = integer_literal $Builtin.Int1, 0 -// CHECK-NEXT: store [[ZERO]] to [[BITMAP_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassC6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 -// CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] -// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): +// CHECK-NEXT: [[LD:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: strong_retain [[LD]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] -// CHECK-NEXT: return [[NEW_SELF]] +// CHECK-NEXT: return [[LD]] // CHECK: bb3([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: br bb5([[ERROR]] : $any Error) +// CHECK-NEXT: br bb5([[ERROR]] : $any Error, [[ZERO]] : $Builtin.Int1) // CHECK: bb4([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: strong_release [[NEW_SELF]] -// CHECK-NEXT: br bb5([[ERROR]] : $any Error) -// CHECK: bb5([[ERROR:%.*]] : $any Error): -// CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] -// CHECK: cond_br {{.*}}, bb6, bb7 +// CHECK-NEXT: br bb5([[ERROR]] : $any Error, [[BIT]] : $Builtin.Int1) +// CHECK: bb5([[ERROR:%.*]] : $any Error, [[COND:%.*]] : $Builtin.Int1): +// CHECK: cond_br [[COND]], bb6, bb7 // CHECK: bb6: // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: br bb8 @@ -1427,7 +1376,6 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: br bb8 // CHECK: bb8: // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] // CHECK-NEXT: throw [[ERROR]] convenience init(failBeforeOrAfterDelegation: Int) throws { try unwrap(failBeforeOrAfterDelegation) diff --git a/test/SILOptimizer/definite_init_failable_initializers_objc.swift b/test/SILOptimizer/definite_init_failable_initializers_objc.swift index f74de9fe05f9f..f64e4c11bb35d 100644 --- a/test/SILOptimizer/definite_init_failable_initializers_objc.swift +++ b/test/SILOptimizer/definite_init_failable_initializers_objc.swift @@ -46,18 +46,17 @@ class Cat : FakeNSObject { // CHECK: bb0([[ARG0:%.*]] : $Int, [[ARG1:%.*]] : $Bool, [[ARG2:%.*]] : $Cat): // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack [var_decl] $Cat // CHECK: store [[ARG2]] to [[SELF_BOX]] : $*Cat - // CHECK: [[FIELD_ADDR:%.*]] = ref_element_addr [[ARG2]] : $Cat, #Cat.x + // CHECK: [[FIELD_ADDR:%.*]] = ref_element_addr {{.*}} : $Cat, #Cat.x // CHECK-NEXT: store {{%.*}} to [[FIELD_ADDR]] : $*LifetimeTracked - // CHECK-NEXT: strong_release [[ARG2]] // CHECK-NEXT: [[COND:%.*]] = struct_extract %1 : $Bool, #Bool._value // CHECK-NEXT: cond_br [[COND]], bb1, bb2 // CHECK: bb1: - // CHECK-NEXT: [[FIELD_ADDR:%.*]] = ref_element_addr [[ARG2]] : $Cat, #Cat.x + // CHECK-NEXT: [[LD:%.*]] = load {{.*}} : $*Cat + // CHECK-NEXT: [[FIELD_ADDR:%.*]] = ref_element_addr [[LD]] : $Cat, #Cat.x // CHECK-NEXT: [[FIELD_ADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[FIELD_ADDR]] // CHECK-NEXT: destroy_addr [[FIELD_ADDR_ACCESS]] : $*LifetimeTracked // CHECK-NEXT: end_access [[FIELD_ADDR_ACCESS]] - // CHECK-NEXT: strong_release [[ARG2]] // CHECK-NEXT: [[RELOAD_FROM_BOX:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thick Cat.Type // CHECK-NEXT: dealloc_partial_ref [[RELOAD_FROM_BOX]] : $Cat, [[METATYPE]] : $@thick Cat.Type @@ -66,7 +65,6 @@ class Cat : FakeNSObject { // CHECK-NEXT: br bb3([[RESULT]] : $Optional) // CHECK: bb2: - // CHECK-NEXT: strong_release [[ARG2]] // CHECK-NEXT: [[RELOAD_ARG2:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[SUPER:%.*]] = upcast [[RELOAD_ARG2]] : $Cat to $FakeNSObject // CHECK-NEXT: [[SUB:%.*]] = unchecked_ref_cast [[RELOAD_ARG2]] : $Cat to $Cat @@ -102,7 +100,6 @@ class Cat : FakeNSObject { // CHECK-LABEL: sil hidden @$s40definite_init_failable_initializers_objc3CatC4fail5afterACSgSb_Sbtcfc : $@convention(method) (Bool, Bool, @owned Cat) -> @owned Optional // CHECK: bb0([[ARG0:%.*]] : $Bool, [[ARG1:%.*]] : $Bool, [[ARG2:%.*]] : $Cat): - // CHECK-NEXT: [[HAS_RUN_INIT_BOX:%.+]] = alloc_stack $Builtin.Int1 // CHECK-NEXT: [[SELF_BOX:%.+]] = alloc_stack [dynamic_lifetime] [var_decl] $Cat // CHECK: store [[ARG2]] to [[SELF_BOX]] : $*Cat // CHECK-NEXT: [[COND:%.+]] = struct_extract [[ARG0]] : $Bool, #Bool._value @@ -132,8 +129,7 @@ class Cat : FakeNSObject { // CHECK-NEXT: dealloc_stack [[SELF_BOX]] : $*Cat // CHECK-NEXT: br [[RESULT_BRANCH:bb[0-9]+]]([[RESULT]] : $Optional) - // CHECK: [[ERROR_BRANCH]]: - // CHECK-NEXT: [[COND:%.+]] = load [[HAS_RUN_INIT_BOX]] : $*Builtin.Int1 + // CHECK: [[ERROR_BRANCH]]([[COND:%.*]] : $Builtin.Int1): // CHECK-NEXT: cond_br [[COND]], [[ERROR_WITHOUT_DESTROY_BRANCH:bb[0-9]+]], [[ERROR_WITH_DESTROY_BRANCH:bb[0-9]+]] // CHECK: [[ERROR_WITHOUT_DESTROY_BRANCH]]: @@ -151,7 +147,6 @@ class Cat : FakeNSObject { // CHECK-NEXT: br [[RESULT_BRANCH]]([[NIL_RESULT]] : $Optional) // CHECK: [[RESULT_BRANCH]]([[RESULT:%.+]] : $Optional): - // CHECK-NEXT: dealloc_stack [[HAS_RUN_INIT_BOX]] : $*Builtin.Int1 // CHECK-NEXT: return [[RESULT]] : $Optional // CHECK: end sil function '$s40definite_init_failable_initializers_objc3CatC4fail5afterACSgSb_Sbtcfc' diff --git a/test/SILOptimizer/definite_init_nsmanagedvalue.swift b/test/SILOptimizer/definite_init_nsmanagedvalue.swift index b81460ba32c01..df4dfefd26a6b 100644 --- a/test/SILOptimizer/definite_init_nsmanagedvalue.swift +++ b/test/SILOptimizer/definite_init_nsmanagedvalue.swift @@ -31,4 +31,6 @@ extension Person { // Verify that the DI instructions share the scope of the adjacent instructions. // CHECK: sil {{.*}}$s28definite_init_nsmanagedvalue6PersonCyACSiKcfc // CHECK: integer_literal $Builtin.Int2, {{.*}}, scope [[SCOPE:[0-9]+]] +// CHECK-NEXT: debug_value +// CHECK-NEXT: debug_value // CHECK-NEXT: store {{.*}}, scope [[SCOPE]] diff --git a/test/SILOptimizer/definite_init_root_class.swift b/test/SILOptimizer/definite_init_root_class.swift index 6667052b893f5..dbaafa83301ab 100644 --- a/test/SILOptimizer/definite_init_root_class.swift +++ b/test/SILOptimizer/definite_init_root_class.swift @@ -13,10 +13,8 @@ class FirstClass { // CHECK-LABEL: sil hidden @$s24definite_init_root_class10FirstClassC1nACSgs5Int32V_tcfc : $@convention(method) (Int32, @owned FirstClass) -> @owned Optional init?(n: Int32) { - // CHECK: [[CONTROL:%.*]] = alloc_stack $Builtin.Int1 // CHECK: [[EI:%.*]] = end_init_let_ref %1 // CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int1, 0 - // CHECK: store [[ZERO]] to [[CONTROL]] : $*Builtin.Int1 // CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int32, 0 // CHECK: [[N:%.*]] = struct_extract %0 : $Int32, #Int32._value @@ -36,7 +34,6 @@ class FirstClass { // CHECK: [[X_ADDR:%.*]] = ref_element_addr [[EI]] : $FirstClass, #FirstClass.x // CHECK: [[X_ACCESS:%.*]] = begin_access [init] [static] [[X_ADDR]] : $*OtherClass // CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int1, -1 - // CHECK: store [[ONE]] to [[CONTROL]] : $*Builtin.Int1 // CHECK: store [[OTHER]] to [[X_ACCESS]] : $*OtherClass // CHECK: end_access [[X_ACCESS]] : $*OtherClass x = OtherClass() @@ -56,8 +53,7 @@ class FirstClass { // CHECK: [[RESULT:%.*]] = enum $Optional, #Optional.some!enumelt, [[EI]] : $FirstClass // CHECK: br bb12([[RESULT]] : $Optional) - // CHECK: bb5: - // CHECK: [[BIT:%.*]] = load [[CONTROL]] : $*Builtin.Int1 + // CHECK: bb5([[BIT:%.*]] : $Builtin.Int1): // CHECK: cond_br [[BIT]], bb6, bb7 // CHECK: bb6: @@ -65,7 +61,6 @@ class FirstClass { // CHECK: br bb11 // CHECK: bb7: - // CHECK: [[BIT:%.*]] = load [[CONTROL]] : $*Builtin.Int1 // CHECK: cond_br [[BIT]], bb8, bb9 // CHECK: bb8: @@ -87,7 +82,6 @@ class FirstClass { // CHECK: br bb12([[NIL]] : $Optional) // CHECK: bb12([[RESULT:%.*]] : $Optional): - // CHECK: dealloc_stack [[CONTROL]] : $*Builtin.Int1 // CHECK: return [[RESULT]] : $Optional } } @@ -98,10 +92,8 @@ class SecondClass { // CHECK-LABEL: sil hidden @$s24definite_init_root_class11SecondClassC1nACSgs5Int32V_tcfc : $@convention(method) (Int32, @owned SecondClass) -> @owned Optional { init?(n: Int32) { - // CHECK: [[CONTROL:%.*]] = alloc_stack $Builtin.Int2 // CHECK: [[EI:%.*]] = end_init_let_ref %1 // CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int2, 0 - // CHECK: store [[ZERO]] to [[CONTROL]] : $*Builtin.Int2 // CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int32, 0 // CHECK: [[N:%.*]] = struct_extract %0 : $Int32, #Int32._value @@ -121,7 +113,6 @@ class SecondClass { // CHECK: [[X_ADDR:%.*]] = ref_element_addr [[EI]] : $SecondClass, #SecondClass.x // CHECK: [[X_ACCESS:%.*]] = begin_access [init] [static] [[X_ADDR]] : $*OtherClass // CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int2, 1 - // CHECK: store [[ONE]] to [[CONTROL]] : $*Builtin.Int2 // CHECK: store [[OTHER]] to [[X_ACCESS]] : $*OtherClass // CHECK: end_access [[X_ACCESS]] : $*OtherClass x = OtherClass() @@ -144,7 +135,6 @@ class SecondClass { // CHECK: [[Y_ADDR:%.*]] = ref_element_addr [[EI]] : $SecondClass, #SecondClass.y // CHECK: [[Y_ACCESS:%.*]] = begin_access [init] [static] [[Y_ADDR]] : $*OtherClass // CHECK: [[THREE:%.*]] = integer_literal $Builtin.Int2, -1 - // CHECK: store [[THREE]] to [[CONTROL]] : $*Builtin.Int2 // CHECK: store [[OTHER]] to [[Y_ACCESS]] : $*OtherClass // CHECK: end_access [[Y_ACCESS]] : $*OtherClass y = OtherClass() @@ -164,8 +154,7 @@ class SecondClass { // CHECK: [[RESULT:%.*]] = enum $Optional, #Optional.some!enumelt, [[EI]] : $SecondClass // CHECK: br bb17([[RESULT]] : $Optional) - // CHECK: bb7: - // CHECK: [[BITS:%.*]] = load [[CONTROL]] : $*Builtin.Int2 + // CHECK: bb7([[BITS:%.*]] : $Builtin.Int2): // CHECK: [[THREE:%.*]] = integer_literal $Builtin.Int2, -1 // CHECK: [[BIT:%.*]] = builtin "cmp_eq_Int2"([[BITS]] : $Builtin.Int2, [[THREE]] : $Builtin.Int2) : $Builtin.Int1 // CHECK: cond_br [[BIT]], bb8, bb9 @@ -175,7 +164,6 @@ class SecondClass { // CHECK: br bb16 // CHECK: bb9: - // CHECK: [[BITS:%.*]] = load [[CONTROL]] : $*Builtin.Int2 // CHECK: [[BIT:%.*]] = builtin "trunc_Int2_Int1"([[BITS]] : $Builtin.Int2) : $Builtin.Int1 // CHECK: cond_br [[BIT]], bb10, bb11 @@ -190,7 +178,6 @@ class SecondClass { // CHECK: br bb12 // CHECK: bb12: - // CHECK: [[BITS:%.*]] = load [[CONTROL]] : $*Builtin.Int2 // CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int2, 1 // CHECK: [[TMP:%.*]] = builtin "lshr_Int2"([[BITS]] : $Builtin.Int2, [[ONE]] : $Builtin.Int2) : $Builtin.Int2 // CHECK: [[BIT:%.*]] = builtin "trunc_Int2_Int1"([[TMP]] : $Builtin.Int2) : $Builtin.Int1 @@ -216,7 +203,6 @@ class SecondClass { // CHECK: br bb17([[NIL]] : $Optional) // CHECK: bb17([[RESULT:%.*]] : $Optional): - // CHECK: dealloc_stack [[CONTROL]] : $*Builtin.Int2 // CHECK: return [[RESULT]] : $Optional } } diff --git a/test/SILOptimizer/definite_init_value_types.swift b/test/SILOptimizer/definite_init_value_types.swift index 395eecff46e6c..283dbec02fa82 100644 --- a/test/SILOptimizer/definite_init_value_types.swift +++ b/test/SILOptimizer/definite_init_value_types.swift @@ -30,26 +30,22 @@ enum ValueEnum { // CHECK-LABEL: sil hidden @$s25definite_init_value_types9ValueEnumO1xACSb_tcfC : $@convention(method) (Bool, @thin ValueEnum.Type) -> @owned ValueEnum // CHECK: bb0(%0 : $Bool, %1 : $@thin ValueEnum.Type): - // CHECK-NEXT: [[STATE:%.*]] = alloc_stack $Builtin.Int1 // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack [dynamic_lifetime] [var_decl] $ValueEnum // CHECK-NEXT: [[INIT_STATE:%.*]] = integer_literal $Builtin.Int1, 0 - // CHECK-NEXT: store [[INIT_STATE]] to [[STATE]] // CHECK: [[BOOL:%.*]] = struct_extract %0 : $Bool, #Bool._value // CHECK-NEXT: cond_br [[BOOL]], bb1, bb2 // CHECK: bb1: // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $ValueEnum, #ValueEnum.b!enumelt // CHECK-NEXT: [[SELF_ACCESS:%.*]] = begin_access [modify] [static] [[SELF_BOX]] // CHECK-NEXT: [[NEW_STATE:%.*]] = integer_literal $Builtin.Int1, -1 - // CHECK-NEXT: store [[NEW_STATE]] to [[STATE]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_ACCESS]] // CHECK-NEXT: end_access [[SELF_ACCESS]] // CHECK-NEXT: br bb3 // CHECK: bb2: // CHECK-NEXT: br bb3 - // CHECK: bb3: + // CHECK: bb3([[STATE_VALUE:%.*]] : $Builtin.Int1): // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $ValueEnum, #ValueEnum.c!enumelt // CHECK-NEXT: [[SELF_ACCESS:%.*]] = begin_access [modify] [static] [[SELF_BOX]] - // CHECK-NEXT: [[STATE_VALUE:%.*]] = load [[STATE]] // CHECK-NEXT: cond_br [[STATE_VALUE]], bb4, bb5 // CHECK: bb4: // CHECK-NEXT: destroy_addr [[SELF_BOX]] @@ -57,13 +53,10 @@ enum ValueEnum { // CHECK: bb5: // CHECK-NEXT: br bb6 // CHECK: bb6: - // CHECK-NEXT: [[NEW_STATE:%.*]] = integer_literal $Builtin.Int1, -1 - // CHECK-NEXT: store [[NEW_STATE]] to [[STATE]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_ACCESS]] // CHECK-NEXT: end_access [[SELF_ACCESS]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] - // CHECK-NEXT: dealloc_stack [[STATE]] // CHECK-NEXT: return [[NEW_SELF]] init(x: Bool) { if x { diff --git a/test/SILOptimizer/let-property-lowering.swift b/test/SILOptimizer/let-property-lowering.swift index e38938a9853ae..1f276dae67dcb 100644 --- a/test/SILOptimizer/let-property-lowering.swift +++ b/test/SILOptimizer/let-property-lowering.swift @@ -67,7 +67,7 @@ class C { // CHECK: [[A:%.*]] = ref_element_addr %1 : $C, #C.a // CHECK: store {{.*}} to [[A]] : $*Int // CHECK: [[EI:%.*]] = end_init_let_ref %1 - // CHECK: bb1: + // CHECK: bb1({{.*}} : $Builtin.Int2): // CHECK: ref_element_addr [[EI]] : $C, #C.b // CHECK: return [[EI]] // CHECK: } // end sil function '$s4test1CC1cACSb_tcfc' @@ -173,7 +173,7 @@ class D : C { // CHECK-LABEL: sil hidden @$s4test1DCACycfc : // CHECK-NOT: end_init_let_ref - // CHECK: ref_element_addr %0 : $D, #D.c + // CHECK: ref_element_addr %{{[0-9]+}} : $D, #D.c // CHECK-NOT: end_init_let_ref // CHECK: } // end sil function '$s4test1DCACycfc' override init() { diff --git a/test/SILOptimizer/mandatory_inlining_devirt.swift b/test/SILOptimizer/mandatory_inlining_devirt.swift index b0ff9f7adb03e..0392f14f6133e 100644 --- a/test/SILOptimizer/mandatory_inlining_devirt.swift +++ b/test/SILOptimizer/mandatory_inlining_devirt.swift @@ -44,9 +44,8 @@ public struct Concrete : Thrower { // CHECK-LABEL: sil @$s4test6calleryyAA8ConcreteVKF : $@convention(thin) (Concrete) -> @error any Error public func caller(_ c: Concrete) throws { - // CHECK: [[ARG:%.*]] = struct $Concrete () // CHECK: [[FN:%.*]] = function_ref @$s4test8ConcreteV4failyyKF : $@convention(method) (Concrete) -> @error any Error - // CHECK: try_apply [[FN]]([[ARG]]) : $@convention(method) (Concrete) -> @error any Error + // CHECK: try_apply [[FN]](%0) : $@convention(method) (Concrete) -> @error any Error try callee(c) } diff --git a/test/SILOptimizer/package-cmo-inlining-pass.swift b/test/SILOptimizer/package-cmo-inlining-pass.swift index 67bb311b04739..bd73a456ce051 100644 --- a/test/SILOptimizer/package-cmo-inlining-pass.swift +++ b/test/SILOptimizer/package-cmo-inlining-pass.swift @@ -45,7 +45,7 @@ package struct PkgStruct { // CHECK: store {{.*}} to [trivial] [[FIELD1_IVAR]] : $*Int // CHECK: [[FIELD2_IVAR:%.*]] = struct_element_addr [[PKG_INIT]] : $*PkgStruct, #PkgStruct.field2 // CHECK: store {{.*}} to [trivial] [[FIELD2_IVAR]] : $*Int -// CHECK: [[PKG_STR:%.*]] = struct $PkgStruct +// CHECK: [[PKG_STR:%.*]] = load [trivial] [[PKG_INIT]] // CHECK: store [[PKG_STR]] to [trivial] [[PKG_ALLOC]] : $*PkgStruct // CHECK: [[FIELD1:%.*]] = struct_element_addr [[PKG_ALLOC]] : $*PkgStruct, #PkgStruct.field1 // CHECK: load [trivial] [[FIELD1]] : $*Int diff --git a/test/SILOptimizer/pointer_conversion_objc.swift b/test/SILOptimizer/pointer_conversion_objc.swift index 964ee15053539..21b80053bf39d 100644 --- a/test/SILOptimizer/pointer_conversion_objc.swift +++ b/test/SILOptimizer/pointer_conversion_objc.swift @@ -1,8 +1,7 @@ // RUN: %target-swift-frontend -module-name pointer_conversion -Xllvm -sil-print-types -emit-sil -O %s | %FileCheck %s -// REQUIRES: optimized_stdlib +// REQUIRES: swift_stdlib_no_asserts,optimized_stdlib // REQUIRES: objc_interop -// REQUIRES: swift_stdlib_asserts // Opaque, unoptimizable functions to call. @_silgen_name("takesConstRawPointer") @@ -29,12 +28,6 @@ public func testOptionalArray() { // CHECK: switch_enum {{.*}}, case #Optional.some!enumelt: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] // CHECK: [[SOME_BB]]( - // CHECK: cond_br {{%.*}}, {{bb[0-9]+}}, [[CHECK_BB:bb[0-9]+]] - - // CHECK: [[CHECK_BB]]: - // CHECK: cond_br {{%.*}}, [[CHECK_BB_2:bb[0-9]+]], {{bb[0-9]+}} - - // CHECK: [[CHECK_BB_2]]: // CHECK: [[ORIGINAL_OWNER:%.*]] = unchecked_ref_cast {{%.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase // CHECK: [[ORIGINAL_OWNER_EXISTENTIAL:%.*]] = init_existential_ref [[ORIGINAL_OWNER]] // CHECK: [[OWNER:%.+]] = enum $Optional, #Optional.some!enumelt, [[ORIGINAL_OWNER_EXISTENTIAL]] diff --git a/test/SILOptimizer/pound_assert.swift b/test/SILOptimizer/pound_assert.swift index 16718e3fd1b5e..13ba0e0c34965 100644 --- a/test/SILOptimizer/pound_assert.swift +++ b/test/SILOptimizer/pound_assert.swift @@ -94,11 +94,11 @@ func test_topLevelEvaluation(topLevelArgument: Int) { var topLevelVar = 1 // expected-warning {{never mutated}} #assert(topLevelVar == 1) - // expected-note @+1 {{cannot evaluate top-level value as constant here}} var topLevelVarConditionallyMutated = 1 - if topLevelVarConditionallyMutated < 0 { + if topLevelArgument < 0 { topLevelVarConditionallyMutated += 1 } + // expected-note @+2 {{cannot evaluate expression as constant here}} // expected-error @+1 {{#assert condition not constant}} #assert(topLevelVarConditionallyMutated == 1) diff --git a/test/SILOptimizer/predictable_memopt_dependence.sil b/test/SILOptimizer/predictable_memopt_dependence.sil index 1a43b43a18053..24724cca98bf7 100644 --- a/test/SILOptimizer/predictable_memopt_dependence.sil +++ b/test/SILOptimizer/predictable_memopt_dependence.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -update-borrowed-from -predictable-memaccess-opts -predictable-deadalloc-elim | %FileCheck %s +// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -update-borrowed-from -mandatory-redundant-load-elimination -predictable-deadalloc-elim | %FileCheck %s sil_stage raw @@ -21,11 +21,6 @@ struct SomeClassPair { // Eliminate the load [copy] // Update the mark_dependence base to the available value. // -// FIXME: This creates an extra copy in order to promote a load to a copy_value. -// Fix predictable memopts so it never introduces new copies. -// A copy_value should only be introduced if the corresponding store can be -// eliminated. -// // TODO: box promotion is unsupported: // - Eliminate the allocation and store // - Update the mark_dependence base to the available value @@ -33,8 +28,7 @@ struct SomeClassPair { // CHECK-LABEL: sil [ossa] @markdep_base_promote_box : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer) -> @owned SomeClass { // CHECK: [[ST:%.*]] = copy_value %0 : $SomeClass // CHECK: store %0 to [init] -// CHECK: [[LD:%.*]] = copy_value [[ST]] : $SomeClass -// CHECK: return [[LD]] +// CHECK: return [[ST]] // CHECK-LABEL: } // end sil function 'markdep_base_promote_box' sil [ossa] @markdep_base_promote_box : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer) -> @owned SomeClass { bb0(%0 : @owned $SomeClass, %1 : $UnsafeMutablePointer): @@ -60,10 +54,8 @@ bb0(%0 : @owned $SomeClass, %1 : $UnsafeMutablePointer): // CHECK-LABEL: sil [ossa] @markdep_base_promote_stack : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer) -> @owned SomeClass { // CHECK: [[ST:%.*]] = copy_value %0 : $SomeClass // CHECK: [[MD:%.*]] = mark_dependence %1 : $UnsafeMutablePointer on %0 : $SomeClass -// CHECK: [[LD:%.*]] = copy_value [[ST]] : $SomeClass -// CHECK: destroy_value [[ST]] : $SomeClass // CHECK: struct_extract [[MD]] : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue -// CHECK: return [[LD]] : $SomeClass +// CHECK: return [[ST]] : $SomeClass // CHECK-LABEL: } // end sil function 'markdep_base_promote_stack' sil [ossa] @markdep_base_promote_stack : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer) -> @owned SomeClass { bb0(%0 : @owned $SomeClass, %1 : $UnsafeMutablePointer): @@ -118,10 +110,8 @@ bb0(%0 : $Int, %1 : $UnsafeMutablePointer): // CHECK: [[CP1:%.*]] = copy_value %0 : $SomeClass // CHECK: store %0 to [init] // CHECK: [[MD:%.*]] = mark_dependence [[CP1]] : $SomeClass on %1 : $SomeClass -// CHECK: [[CP2:%.*]] = copy_value [[MD]] : $SomeClass -// CHECK: destroy_value [[MD]] : $SomeClass // CHECK: destroy_value %{{.*}} : $<τ_0_0> { var τ_0_0 } -// CHECK: return [[CP2]] : $SomeClass +// CHECK: return [[MD]] : $SomeClass // CHECK-LABEL: } // end sil function 'markdep_source_promote_box' sil [ossa] @markdep_source_promote_box : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> @owned SomeClass { bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass): @@ -141,11 +131,10 @@ bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass): // // CHECK-LABEL: sil [ossa] @markdep_source_promote_stack : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> @owned SomeClass { // CHECK: [[CP1:%.*]] = copy_value %0 : $SomeClass +// CHECK: [[MD_OLD:%.*]] = mark_dependence %0 : $SomeClass on %1 : $SomeClass // CHECK: [[MD:%.*]] = mark_dependence [[CP1]] : $SomeClass on %1 : $SomeClass -// CHECK: [[CP2:%.*]] = copy_value [[MD]] : $SomeClass -// CHECK: destroy_value [[MD]] : $SomeClass -// CHECK: destroy_value %0 : $SomeClass -// CHECK: return [[CP2]] : $SomeClass +// CHECK: destroy_value [[MD_OLD]] : $SomeClass +// CHECK: return [[MD]] : $SomeClass // CHECK-LABEL: } // end sil function 'markdep_source_promote_stack' sil [ossa] @markdep_source_promote_stack: $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> @owned SomeClass { bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass): @@ -246,10 +235,8 @@ bb0(%0 : @owned $SomeClass, %1 : $UnsafeMutablePointer): // CHECK: [[CP1:%.*]] = copy_value %0 : $SomeClass // CHECK: store %0 to [init] // CHECK: [[MD:%.*]] = mark_dependence [[CP1]] : $SomeClass on %1 : $SomeClass -// CHECK: [[CP2:%.*]] = copy_value [[MD]] : $SomeClass -// CHECK: destroy_value [[MD]] : $SomeClass // CHECK: destroy_value %{{.*}} : $<τ_0_0> { var τ_0_0 } -// CHECK: destroy_value [[CP2]] : $SomeClass +// CHECK: destroy_value [[MD]] : $SomeClass // CHECK-LABEL: } // end sil function 'markdep_source_dead_box' sil [ossa] @markdep_source_dead_box : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> () { bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass): @@ -271,11 +258,10 @@ bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass): // CHECK-LABEL: sil [ossa] @markdep_source_dead_stack : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> () { // CHECK: bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass): // CHECK-NEXT: [[CP1:%.*]] = copy_value %0 : $SomeClass +// CHECK-NEXT: [[MD_OLD:%.*]] = mark_dependence %0 : $SomeClass on %1 : $SomeClass // CHECK-NEXT: [[MD:%.*]] = mark_dependence [[CP1]] : $SomeClass on %1 : $SomeClass -// CHECK-NEXT: [[CP2:%.*]] = copy_value [[MD]] : $SomeClass +// CHECK-NEXT: destroy_value [[MD_OLD]] : $SomeClass // CHECK-NEXT: destroy_value [[MD]] : $SomeClass -// CHECK-NEXT: destroy_value %0 : $SomeClass -// CHECK-NEXT: destroy_value [[CP2]] : $SomeClass // CHECK-LABEL: } // end sil function 'markdep_source_dead_stack' sil [ossa] @markdep_source_dead_stack: $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> () { bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass): @@ -362,16 +348,20 @@ bb0(%0 : @owned $SomeClass, %1 : @owned $SomeClass, %2 : @guaranteed $SomeClass) // Test mark_dependence source: load from dependent phi value. // -// TODO: Phis are not handled by load promotion. -// -// CHECK-LABEL: sil [ossa] @markdep_source_load_phi : $@convention(thin) (@guaranteed SomeClass) -> () { -// CHECK: bb0(%0 : @guaranteed $SomeClass): -// CHECK: [[ALLOC:%.*]] = alloc_stack $SomeClass -// CHECK: [[MD:%.*]] = mark_dependence [[ALLOC]] : $*SomeClass on %0 : $SomeClass -// CHECK: [[LD:%.*]] = load [copy] [[MD]] : $*SomeClass -// CHECK: destroy_addr %1 : $*SomeClass +// CHECK-LABEL: sil [ossa] @markdep_source_load_phi : +// CHECK: bb0(%0 : @guaranteed $SomeClass): +// CHECK-NEXT: [[CP0:%.*]] = copy_value %0 : $SomeClass +// CHECK: bb1: +// CHECK-NEXT: [[CP1:%.*]] = copy_value [[CP0]] +// CHECK-NEXT: br bb3([[CP1]] : $SomeClass) +// CHECK: bb2: +// CHECK-NEXT: [[CP2:%.*]] = copy_value [[CP0]] +// CHECK-NEXT: br bb3([[CP2]] : $SomeClass) +// CHECK: bb3([[PHI:%.*]] : @owned $SomeClass): +// CHECK: [[MD:%.*]] = mark_dependence [[PHI]] : $SomeClass on %0 : $SomeClass +// CHECK: return [[MD]] // CHECK-LABEL: } // end sil function 'markdep_source_load_phi' -sil [ossa] @markdep_source_load_phi : $@convention(thin) (@guaranteed SomeClass) -> () { +sil [ossa] @markdep_source_load_phi : $@convention(thin) (@guaranteed SomeClass) -> @owned SomeClass { bb0(%0 : @guaranteed $SomeClass): %1 = alloc_stack $SomeClass %copy = copy_value %0 : $SomeClass @@ -388,11 +378,9 @@ bb2: bb3: %md = mark_dependence %1 : $*SomeClass on %0 : $SomeClass %ld = load [copy] %md : $*SomeClass - destroy_value %ld : $SomeClass destroy_addr %1 : $*SomeClass dealloc_stack %1 : $*SomeClass - %9999 = tuple() - return %9999 : $() + return %ld : $SomeClass } // Test mark_dependence source: dead dependent phi. diff --git a/test/SILOptimizer/predictable_memopt_locs.swift b/test/SILOptimizer/predictable_memopt_locs.swift index db43ee8569586..6a3295912eef3 100644 --- a/test/SILOptimizer/predictable_memopt_locs.swift +++ b/test/SILOptimizer/predictable_memopt_locs.swift @@ -14,8 +14,6 @@ public func main() { // CHECK-SAME: loc {{.*}}:11:10, scope [[S:[0-9]+]] // CHECK-NEXT: %[[I:.*]] = struct_extract %[[A]] // CHECK-SAME: loc {{.*}}:11:10, scope [[S]] - // CHECK-NEXT: struct_extract %[[I]] - // CHECK-SAME: loc {{.*}}:11:10, scope [[S]] // CHECK: store %[[A]] to %0 : $*MyStruct, // CHECK-SAME: loc {{.*}}:11:10, scope [[S]] use(a.a) diff --git a/test/SILOptimizer/predictable_memopt_ownership.sil b/test/SILOptimizer/predictable_memopt_ownership.sil index 71ad037502129..ab61f4b45c2fb 100644 --- a/test/SILOptimizer/predictable_memopt_ownership.sil +++ b/test/SILOptimizer/predictable_memopt_ownership.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -update-borrowed-from -predictable-memaccess-opts -predictable-deadalloc-elim | %FileCheck %s +// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -update-borrowed-from -mandatory-redundant-load-elimination -predictable-deadalloc-elim | %FileCheck %s sil_stage raw @@ -146,8 +146,8 @@ bb0(%1 : $Int): store %1 to [trivial] %0a : $*Int // CHECK: store - // In an escape region, we should not promote loads. - %r = load [trivial] %0a : $*Int // CHECK: load + %r = load [trivial] %0a : $*Int + // CHECK: return %0 return %r : $Int } @@ -216,12 +216,8 @@ bb0(%0 : @owned $ContainsNativeObject): // CHECK: [[f3:%.*]] = struct_extract [[BORROWED_ARG]] : $ComplexStruct, #ComplexStruct.f1 // CHECK: [[f3_copy:%.*]] = copy_value [[f3]] // CHECK: end_borrow [[BORROWED_ARG]] -// CHECK: [[f3_copy_1:%.*]] = copy_value [[f3_copy]] -// CHECK: [[f3_copy_2:%.*]] = copy_value [[f3_copy_1]] -// CHECK: [[f2_x_copy_1:%.*]] = copy_value [[f2_x_copy]] -// CHECK: [[f2_x_copy_2:%.*]] = copy_value [[f2_x_copy_1]] // CHECK: destroy_value [[ARG]] -// CHECK: [[RESULT:%.*]] = tuple ([[f3_copy_2]] : $Builtin.NativeObject, [[f2_x_copy_2]] : $Builtin.NativeObject, [[f1]] : $Builtin.Int32) +// CHECK: [[RESULT:%.*]] = tuple ([[f3_copy]] : $Builtin.NativeObject, [[f2_x_copy]] : $Builtin.NativeObject, [[f1]] : $Builtin.Int32) // CHECK: return [[RESULT]] // CHECK: } // end sil function 'multiple_level_extract_2' sil [ossa] @multiple_level_extract_2 : $@convention(thin) (@owned ComplexStruct) -> (@owned Builtin.NativeObject, @owned Builtin.NativeObject, Builtin.Int32) { @@ -248,21 +244,24 @@ bb0(%0 : @owned $ComplexStruct): var int_global : Int // CHECK-LABEL: sil [ossa] @promote_alloc_stack +// CHECK: [[IL:%.*]] = integer_literal +// CHECK: [[I:%.*]] = struct $Int32 ([[IL]] : $Builtin.Int32) +// CHECK-NOT: alloc_stack +// CHECK: [[E:%.*]] = struct_extract [[I]] +// CHECK: return [[E]] +// CHECK: } // end sil function 'promote_alloc_stack' sil [ossa] @promote_alloc_stack : $@convention(thin) (Int32) -> Builtin.Int32 { bb0(%0 : $Int32): %5 = integer_literal $Builtin.Int32, 1 - // CHECK: [[IL:%[0-9]+]] = integer_literal %18 = struct $Int32 (%5 : $Builtin.Int32) %22 = alloc_stack $Int32 - // CHECK-NOT: alloc_stack store %18 to [trivial] %22 : $*Int32 %24 = struct_element_addr %22 : $*Int32, #Int32._value %25 = load [trivial] %24 : $*Builtin.Int32 dealloc_stack %22 : $*Int32 - // CHECK-NEXT: return [[IL]] return %25 : $Builtin.Int32 } @@ -430,39 +429,35 @@ bb0: } // Predictable memory opts removes refcount operation +// TODO: this is currently not optimized // CHECK-LABEL: sil [ossa] @dead_allocation_1 sil [ossa] @dead_allocation_1 : $@convention(thin) (@owned Optional) -> () { bb0(%0 : @owned $Optional): -// CHECK-NOT: alloc_stack %1 = alloc_stack $Optional %2 = alloc_stack $Optional store %0 to [init] %2 : $*Optional -// CHECK-NOT: copy_addr copy_addr %2 to [init] %1 : $*Optional destroy_addr %2 : $*Optional dealloc_stack %2 : $*Optional destroy_addr %1 : $*Optional dealloc_stack %1 : $*Optional -// CHECK: destroy_value %0 %3 = tuple () return %3 : $() } +// TODO: this is currently not optimized // CHECK-LABEL: sil [ossa] @dead_allocation_2 sil [ossa] @dead_allocation_2 : $@convention(thin) (@owned Optional) -> () { bb0(%0 : @owned $Optional): -// CHECK-NOT: alloc_stack %1 = alloc_stack $Optional %2 = alloc_stack $Optional store %0 to [init] %1 : $*Optional -// CHECK-NOT: copy_addr copy_addr %1 to [init] %2 : $*Optional destroy_addr %2 : $*Optional dealloc_stack %2 : $*Optional destroy_addr %1 : $*Optional dealloc_stack %1 : $*Optional %3 = tuple () -// CHECK: destroy_value %0 return %3 : $() } @@ -557,32 +552,13 @@ bb3: // This test makes sure that we insert the tuple_extracts that we need before // the store in bb0, not at the load block. +// TODO: this is currently not optimized // CHECK-LABEL: sil [ossa] @diamond_test_2 : $@convention(thin) (@owned NativeObjectPair) -> @owned Builtin.NativeObject { -// CHECK: bb0([[ARG:%.*]] : @owned $NativeObjectPair): -// CHECK: [[BORROWED_ARG:%.*]] = begin_borrow [[ARG]] -// CHECK: [[LHS1:%.*]] = struct_extract [[BORROWED_ARG]] : $NativeObjectPair, #NativeObjectPair.x -// CHECK: [[LHS1_COPY:%.*]] = copy_value [[LHS1]] -// CHECK: [[BORROWED_ARG:%.*]] = begin_borrow [[ARG]] -// CHECK: [[LHS2:%.*]] = struct_extract [[BORROWED_ARG]] : $NativeObjectPair, #NativeObjectPair.x -// CHECK: [[LHS2_COPY:%.*]] = copy_value [[LHS2]] -// CHECK: cond_br undef, bb1, bb2 -// -// CHECK: bb1: -// CHECK: destroy_value [[LHS1_COPY]] -// CHECK: [[LHS2_COPY_1:%.*]] = copy_value [[LHS2_COPY]] -// CHECK: [[LHS2_COPY_2:%.*]] = copy_value [[LHS2_COPY_1]] -// CHECK: br bb3([[LHS2_COPY_2]] : -// -// CHECK: bb2: -// CHECK: destroy_value [[LHS2_COPY]] : $Builtin.NativeObject -// CHECK: [[LHS1_COPY_1:%.*]] = copy_value [[LHS1_COPY]] -// CHECK: [[LHS1_COPY_2:%.*]] = copy_value [[LHS1_COPY_1]] -// CHECK: br bb3([[LHS1_COPY_2]] : -// -// CHECK: bb3([[PHI:%.*]] : -// CHECK: destroy_value [[ARG]] -// CHECK: return [[PHI]] -// CHECK: } // end sil function 'diamond_test_2' +// CHECK: bb1: +// CHECK: load [copy] +// CHECK: bb2: +// CHECK: load [copy] +// CHECK: } // end sil function 'diamond_test_2' sil [ossa] @diamond_test_2 : $@convention(thin) (@owned NativeObjectPair) -> @owned Builtin.NativeObject { bb0(%0 : @owned $NativeObjectPair): %1 = alloc_stack $NativeObjectPair @@ -606,12 +582,14 @@ bb3(%6 : @owned $Builtin.NativeObject): } // We should be able to promote all memory operations here. +// TODO: this is currently not optimized // // CHECK-LABEL: sil [ossa] @diamond_test_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned Builtin.NativeObject { -// CHECK-NOT: alloc_stack -// CHECK-NOT: load -// CHECK-NOT: store -// CHECK: } // end sil function 'diamond_test_3' +// CHECK: bb1: +// CHECK: load [copy] +// CHECK: bb2: +// CHECK: load [copy] +// CHECK: } // end sil function 'diamond_test_3' sil [ossa] @diamond_test_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): %2 = alloc_stack $NativeObjectPair @@ -663,11 +641,9 @@ struct NativeObjectTriple { // CHECK-NEXT: br bb3([[PAIR_LHS_COPY]] : // // CHECK: bb3([[PHI:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[PHI_COPY_1:%.*]] = copy_value [[PHI]] -// CHECK: [[PHI_COPY_2:%.*]] = copy_value [[PHI_COPY_1]] // CHECK: [[REFORMED:%.*]] = struct $NativeObjectTriple ([[ARG0]] : {{.*}}, [[ARG1]] : {{.*}}) // CHECK: destroy_value [[REFORMED]] -// CHECK: return [[PHI_COPY_2]] +// CHECK: return [[PHI]] // CHECK: } // end sil function 'diamond_test_4' sil [ossa] @diamond_test_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair): @@ -699,42 +675,12 @@ bb3: // Make sure that we do the right thing if our definite init value is partially // overridden along one path +// TODO: this is currently not optimized // // CHECK-LABEL: sil [ossa] @diamond_test_5 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair, @owned Builtin.NativeObject) -> @owned NativeObjectPair { -// CHECK: bb0([[ARG0:%.*]] : @owned $Builtin.NativeObject, [[ARG1:%.*]] : @owned $NativeObjectPair, [[ARG2:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[BOX:%.*]] = alloc_stack $NativeObjectTriple -// CHECK: br bb1 -// -// CHECK: bb1: -// CHECK: [[TRIPLE_LHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f1 -// CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 -// CHECK: store [[ARG0]] to [init] [[TRIPLE_LHS]] -// CHECK: [[BORROWED_ARG1:%.*]] = begin_borrow [[ARG1]] -// CHECK: [[BORROWED_TRIPLE_RHS_RHS_VAL:%.*]] = struct_extract [[BORROWED_ARG1]] : $NativeObjectPair, #NativeObjectPair.y -// CHECK: [[TRIPLE_RHS_RHS_VAL:%.*]] = copy_value [[BORROWED_TRIPLE_RHS_RHS_VAL]] -// CHECK: store [[ARG1]] to [init] [[TRIPLE_RHS]] -// CHECK: cond_br undef, bb2, bb3 -// -// CHECK: bb2: -// CHECK: [[TRIPLE_RHS_LHS:%.*]] = struct_element_addr [[TRIPLE_RHS]] -// CHECK: store [[ARG2]] to [assign] [[TRIPLE_RHS_LHS]] -// CHECK: br bb4 -// -// CHECK: bb3: -// CHECK: br bb4 -// -// CHECK: bb4: -// CHECK: [[TRIPLE_RHS_LHS:%.*]] = struct_element_addr [[TRIPLE_RHS]] : $*NativeObjectPair, #NativeObjectPair.x -// CHECK: [[TRIPLE_RHS_LHS_VAL:%.*]] = load [copy] [[TRIPLE_RHS_LHS]] : $*Builtin.NativeObject -// CHECK: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK: [[TRIPLE_RHS_LHS_VAL_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_LHS_VAL]] -// CHECK: [[TRIPLE_RHS_RHS_VAL_COPY_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_RHS_VAL_COPY]] -// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL_BORROW]] : {{.*}}, [[TRIPLE_RHS_RHS_VAL_COPY_BORROW]] : {{.*}}) -// CHECK: [[STRUCT_COPY:%.*]] = copy_value [[STRUCT]] -// CHECK: [[STRUCT_COPY_2:%.*]] = copy_value [[STRUCT_COPY]] -// CHECK: destroy_addr [[BOX]] -// CHECK: return [[STRUCT_COPY_2]] -// CHECK: } // end sil function 'diamond_test_5' +// CHECK: bb4: +// CHECK: load [copy] +// CHECK: } // end sil function 'diamond_test_5' sil [ossa] @diamond_test_5 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair, %arg2 : @owned $Builtin.NativeObject): %2 = alloc_stack $NativeObjectTriple @@ -763,74 +709,11 @@ bb4: return %13 : $NativeObjectPair } +// TODO: this is currently not optimized // CHECK-LABEL: sil [ossa] @diamond_test_6 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair, @owned Builtin.NativeObject) -> @owned NativeObjectPair { -// CHECK: bb0([[ARG0:%.*]] : @owned $Builtin.NativeObject, [[ARG1:%.*]] : @owned $NativeObjectPair, [[ARG2:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[BOX:%.*]] = alloc_stack $NativeObjectTriple -// CHECK: cond_br undef, [[TRUE_BB:bb[0-9]+]], [[FALSE_BB:bb[0-9]+]] -// -// CHECK: [[TRUE_BB]]: -// CHECK: [[TRIPLE_LHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f1 -// CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 -// CHECK: store [[ARG0]] to [init] [[TRIPLE_LHS]] -// CHECK: [[BORROWED_ARG1:%.*]] = begin_borrow [[ARG1]] -// CHECK: [[BORROWED_TRIPLE_RHS_RHS_VAL:%.*]] = struct_extract [[BORROWED_ARG1]] : $NativeObjectPair, #NativeObjectPair.y -// CHECK: [[TRIPLE_RHS_RHS_VAL:%.*]] = copy_value [[BORROWED_TRIPLE_RHS_RHS_VAL]] -// CHECK: store [[ARG1]] to [init] [[TRIPLE_RHS]] -// CHECK: cond_br undef, [[CRITEDGE_BREAK_BB_1:bb[0-9]+]], [[CRITEDGE_BREAK_BB_2:bb[0-9]+]] -// -// CHECK: [[CRITEDGE_BREAK_BB_1]]: -// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: br [[SUCC_2:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL_COPY]] : -// -// CHECK: [[CRITEDGE_BREAK_BB_2]]: -// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: br [[SUCC_1:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL_COPY]] : -// -// CHECK: [[FALSE_BB]]: -// CHECK: [[TRIPLE_LHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f1 -// CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 -// CHECK: store [[ARG0]] to [init] [[TRIPLE_LHS]] -// CHECK: [[BORROWED_ARG1:%.*]] = begin_borrow [[ARG1]] -// CHECK: [[BORROWED_TRIPLE_RHS_RHS_VAL:%.*]] = struct_extract [[BORROWED_ARG1]] : $NativeObjectPair, #NativeObjectPair.y -// CHECK: [[TRIPLE_RHS_RHS_VAL:%.*]] = copy_value [[BORROWED_TRIPLE_RHS_RHS_VAL]] -// CHECK: store [[ARG1]] to [init] [[TRIPLE_RHS]] -// CHECK: cond_br undef, [[CRITEDGE_BREAK_BB_1:bb[0-9]+]], [[CRITEDGE_BREAK_BB_2:bb[0-9]+]] -// -// CHECK: [[CRITEDGE_BREAK_BB_1]]: -// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: br [[SUCC_2]]([[TRIPLE_RHS_RHS_VAL_COPY]] : -// -// CHECK: [[CRITEDGE_BREAK_BB_2]]: -// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: br [[SUCC_1]]([[TRIPLE_RHS_RHS_VAL_COPY]] : -// -// CHECK: [[SUCC_2]]([[PHI1:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 -// CHECK: [[TRIPLE_RHS_LHS:%.*]] = struct_element_addr [[TRIPLE_RHS]] -// CHECK: store [[ARG2]] to [assign] [[TRIPLE_RHS_LHS]] -// CHECK: br [[EXIT_BB:bb[0-9]+]]([[PHI1:%.*]] : $Builtin.NativeObject) -// -// CHECK: [[SUCC_1]]([[PHI:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[PHI_COPY:%.*]] = copy_value [[PHI]] -// CHECK: br [[EXIT_BB]]([[PHI_COPY]] : {{.*}}) -// -// CHECK: [[EXIT_BB]]([[PHI:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 -// CHECK: [[TRIPLE_RHS_LHS:%.*]] = struct_element_addr [[TRIPLE_RHS]] : $*NativeObjectPair, #NativeObjectPair.x -// CHECK: [[TRIPLE_RHS_LHS_VAL:%.*]] = load [copy] [[TRIPLE_RHS_LHS]] : $*Builtin.NativeObject -// CHECK: [[PHI_COPY:%.*]] = copy_value [[PHI]] -// CHECK: [[TRIPLE_RHS_LHS_VAL_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_LHS_VAL]] -// CHECK: [[PHI_COPY_BORROW:%.*]] = begin_borrow [[PHI_COPY]] -// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL_BORROW]] : {{.*}}, [[PHI_COPY_BORROW]] : {{.*}}) -// CHECK: [[STRUCT_COPY_1:%.*]] = copy_value [[STRUCT]] -// CHECK: [[STRUCT_COPY_2:%.*]] = copy_value [[STRUCT_COPY_1]] -// CHECK: destroy_addr [[BOX]] -// CHECK: return [[STRUCT_COPY_2]] -// CHECK: } // end sil function 'diamond_test_6' +// CHECK: bb9: +// CHECK: load [copy] +// CHECK: } // end sil function 'diamond_test_6' sil [ossa] @diamond_test_6 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair, %arg2 : @owned $Builtin.NativeObject): %2 = alloc_stack $NativeObjectTriple @@ -1027,9 +910,10 @@ bb0(%0 : @owned $NativeObjectPair, %1 : @owned $Builtin.NativeObject): } // Loop case. +// TODO: this is currently not optimized // CHECK-LABEL: sil [ossa] @promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'promote_with_loop_1' +// CHECK: load [copy] +// CHECK: } // end sil function 'promote_with_loop_1' sil [ossa] @promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { bb0(%0 : @owned $NativeObjectPair): %1 = alloc_stack $NativeObjectPair @@ -1045,9 +929,10 @@ bb2: br bb2 } +// TODO: this is currently not optimized // CHECK-LABEL: sil [ossa] @promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'promote_with_loop_2' +// CHECK: load [copy] +// CHECK: } // end sil function 'promote_with_loop_2' sil [ossa] @promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { bb0(%0 : @owned $NativeObjectPair): %1 = alloc_stack $NativeObjectPair @@ -1072,9 +957,13 @@ bb4: return %9999 : $() } +// TODO: this is currently not optimized // CHECK-LABEL: sil [ossa] @two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'two_backedge_loop' +// CHECK: bb3: +// CHECK: load [copy] +// CHECK: bb4: +// CHECK: load [copy] +// CHECK: } // end sil function 'two_backedge_loop' sil [ossa] @two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): %1 = alloc_stack $Builtin.NativeObject @@ -1116,14 +1005,13 @@ bb9: return %9999 : $() } -// Make sure that we can eliminate the allocation and all loads. The key here is -// that we are verifying that we first eliminate load [copy] and treat the load -// [take] as part of the destroy_addr optimization. -// +// TODO: this is currently not optimized // CHECK-LABEL: sil [ossa] @two_backedge_loop_with_take : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: alloc_stack -// CHECK-NOT: load -// CHECK: } // end sil function 'two_backedge_loop_with_take' +// CHECK: bb3: +// CHECK: load [copy] +// CHECK: bb4: +// CHECK: load [copy] +// CHECK: } // end sil function 'two_backedge_loop_with_take' sil [ossa] @two_backedge_loop_with_take : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): %1 = alloc_stack $Builtin.NativeObject From 33761454b71635ea55ae40cf89d6a419f3010cb6 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 6 Feb 2025 15:51:24 +0100 Subject: [PATCH 6/8] MandatoryPerformanceOptimizations: use `eliminateRedundantLoads` instead of `optimizeMemoryAccesses` to optimize redundant loads --- .../MandatoryPerformanceOptimizations.swift | 16 +++++++++++++++- .../Sources/Optimizer/PassManager/Context.swift | 8 -------- include/swift/SILOptimizer/OptimizerBridging.h | 1 - .../swift/SILOptimizer/OptimizerBridgingImpl.h | 3 --- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift index 0e397471e9528..a61c4256480d5 100644 --- a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift +++ b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift @@ -159,6 +159,14 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu { fri.referencedFunction.set(linkage: .public, moduleContext) } + + case let copy as CopyAddrInst: + if function.isGlobalInitOnceFunction, copy.source.type.isLoadable(in: function) { + // In global init functions we have to make sure that redundant load elimination can remove all + // loads (from temporary stack locations) so that globals can be statically initialized. + // For this it's necessary to load copy_addr instructions to loads and stores. + copy.replaceWithLoadAndStore(simplifyCtxt) + } default: break @@ -170,7 +178,13 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu removeUnusedMetatypeInstructions(in: function, context) // If this is a just specialized function, try to optimize copy_addr, etc. - changed = context.optimizeMemoryAccesses(in: function) || changed + if eliminateRedundantLoads(in: function, + variant: function.isGlobalInitOnceFunction ? .mandatoryInGlobalInit : .mandatory, + context) + { + changed = true + } + changed = context.eliminateDeadAllocations(in: function) || changed } } diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift index 2b9f8b62d60ff..496ed1b6c4468 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift @@ -346,14 +346,6 @@ struct FunctionPassContext : MutatingContext { _bridged.asNotificationHandler().notifyChanges(.effectsChanged) } - func optimizeMemoryAccesses(in function: Function) -> Bool { - if _bridged.optimizeMemoryAccesses(function.bridged) { - notifyInstructionsChanged() - return true - } - return false - } - func eliminateDeadAllocations(in function: Function) -> Bool { if _bridged.eliminateDeadAllocations(function.bridged) { notifyInstructionsChanged() diff --git a/include/swift/SILOptimizer/OptimizerBridging.h b/include/swift/SILOptimizer/OptimizerBridging.h index f98d301f89db0..82ef4f3403c68 100644 --- a/include/swift/SILOptimizer/OptimizerBridging.h +++ b/include/swift/SILOptimizer/OptimizerBridging.h @@ -259,7 +259,6 @@ struct BridgedPassContext { BridgedLinkage linkage, bool isLet) const; void inlineFunction(BridgedInstruction apply, bool mandatoryInline) const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedValue getSILUndef(BridgedType type) const; - BRIDGED_INLINE bool optimizeMemoryAccesses(BridgedFunction f) const; BRIDGED_INLINE bool eliminateDeadAllocations(BridgedFunction f) const; BRIDGED_INLINE bool shouldExpand(BridgedType type) const; diff --git a/include/swift/SILOptimizer/OptimizerBridgingImpl.h b/include/swift/SILOptimizer/OptimizerBridgingImpl.h index 51ef3a1da51c0..63cf5eb82ffac 100644 --- a/include/swift/SILOptimizer/OptimizerBridgingImpl.h +++ b/include/swift/SILOptimizer/OptimizerBridgingImpl.h @@ -260,9 +260,6 @@ BridgedValue BridgedPassContext::getSILUndef(BridgedType type) const { return {swift::SILUndef::get(invocation->getFunction(), type.unbridged())}; } -bool BridgedPassContext::optimizeMemoryAccesses(BridgedFunction f) const { - return swift::optimizeMemoryAccesses(f.getFunction()); -} bool BridgedPassContext::eliminateDeadAllocations(BridgedFunction f) const { return swift::eliminateDeadAllocations(f.getFunction(), this->getDomTree().di); From 0a011cddd89a300575727e6e6c60e3e08e728a50 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 6 Feb 2025 15:54:44 +0100 Subject: [PATCH 7/8] Remove the old (and now obsolete) PredictableMemoryAccessOptimizations pass --- .../swift/SILOptimizer/PassManager/Passes.def | 2 - .../swift/SILOptimizer/Utils/InstOptUtils.h | 5 - .../Mandatory/PredictableMemOpt.cpp | 820 ------------------ 3 files changed, 827 deletions(-) diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 0171ffe6714d3..66cce0ec1b074 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -353,8 +353,6 @@ PASS(PerformanceConstantPropagation, "performance-constant-propagation", "Constant Propagation for Performance without Diagnostics") PASS(PerformanceDiagnostics, "performance-diagnostics", "Constant Propagation for Performance without Diagnostics") -PASS(PredictableMemoryAccessOptimizations, "predictable-memaccess-opts", - "Predictable Memory Access Optimizations for Diagnostics") PASS(PredictableDeadAllocationElimination, "predictable-deadalloc-elim", "Eliminate dead temporary allocations after diagnostics") PASS(RedundantPhiElimination, "redundant-phi-elimination", diff --git a/include/swift/SILOptimizer/Utils/InstOptUtils.h b/include/swift/SILOptimizer/Utils/InstOptUtils.h index 533c0c0acbf25..4a7749870c380 100644 --- a/include/swift/SILOptimizer/Utils/InstOptUtils.h +++ b/include/swift/SILOptimizer/Utils/InstOptUtils.h @@ -590,11 +590,6 @@ bool tryEliminateOnlyOwnershipUsedForwardingInst( IntegerLiteralInst *optimizeBuiltinCanBeObjCClass(BuiltinInst *bi, SILBuilder &builder); -/// Performs "predictable" memory access optimizations. -/// -/// See the PredictableMemoryAccessOptimizations pass. -bool optimizeMemoryAccesses(SILFunction *fn); - /// Performs "predictable" dead allocation optimizations. /// /// See the PredictableDeadAllocationElimination pass. diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index d0b65fcd3ac08..37a4bd11b301a 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -41,7 +41,6 @@ using namespace swift; static llvm::cl::opt EnableAggressiveExpansionBlocking( "enable-aggressive-expansion-blocking", llvm::cl::init(false)); -STATISTIC(NumLoadPromoted, "Number of loads promoted"); STATISTIC(NumLoadTakePromoted, "Number of load takes promoted"); STATISTIC(NumDestroyAddrPromoted, "Number of destroy_addrs promoted"); STATISTIC(NumAllocRemoved, "Number of allocations completely removed"); @@ -497,12 +496,6 @@ struct AvailableValueDataflowFixup: AvailableValueFixup { // Clears insertedInsts. void verifyOwnership(DeadEndBlocks &deBlocks); - // Fix ownership of inserted instructions and delete dead instructions. - // - // Clears insertedInsts. - void fixupOwnership(InstructionDeleter &deleter, - DeadEndBlocks &deBlocks); - // Deletes all insertedInsts without fixing ownership. // Clears insertedInsts. void deleteInsertedInsts(InstructionDeleter &deleter); @@ -549,32 +542,6 @@ void AvailableValueDataflowFixup::verifyOwnership(DeadEndBlocks &deBlocks) { insertedInsts.clear(); } -// In OptimizationMode::PreserveAlloc, delete any inserted instructions that are -// still dead and fix ownership of any live inserted copies or casts -// (mark_dependence). -void AvailableValueDataflowFixup::fixupOwnership(InstructionDeleter &deleter, - DeadEndBlocks &deBlocks) { - for (auto *inst : insertedInsts) { - if (inst->isDeleted()) - continue; - - deleter.deleteIfDead(inst); - } - auto *function = const_cast(deBlocks.getFunction()); - OSSALifetimeCompletion completion(function, /*DomInfo*/ nullptr, deBlocks); - for (auto *inst : insertedInsts) { - if (inst->isDeleted()) - continue; - - // If any inserted instruction was not removed, complete its lifetime. - for (auto result : inst->getResults()) { - completion.completeOSSALifetime( - result, OSSALifetimeCompletion::Boundary::Liveness); - } - } - insertedInsts.clear(); -} - void AvailableValueDataflowFixup:: deleteInsertedInsts(InstructionDeleter &deleter) { for (auto *inst : insertedInsts) { @@ -594,11 +561,6 @@ struct AvailableValueAggregationFixup: AvailableValueFixup { /// The list of phi nodes inserted by the SSA updater. SmallVector insertedPhiNodes; - /// A set of copy_values whose lifetime we balanced while inserting phi - /// nodes. This means that these copy_value must be skipped in - /// addMissingDestroysForCopiedValues. - SmallPtrSet copyValueProcessedWithPhiNodes; - AvailableValueAggregationFixup(DeadEndBlocks &deadEndBlocks) : deadEndBlocks(deadEndBlocks) {} @@ -610,29 +572,12 @@ struct AvailableValueAggregationFixup: AvailableValueFixup { SILInstruction *availableAtInst, bool isFullyAvailable); - /// Call this after mergeSingleValueCopies() or mergeAggregateCopies(). - void fixupOwnership(SILInstruction *load, SILValue newVal) { - addHandOffCopyDestroysForPhis(load, newVal); - - // TODO: use OwnershipLifetimeCompletion instead. - addMissingDestroysForCopiedValues(load, newVal); - - insertedInsts.clear(); - insertedPhiNodes.clear(); - copyValueProcessedWithPhiNodes.clear(); - } - private: /// As a result of us using the SSA updater, insert hand off copy/destroys at /// each phi and make sure that intermediate phis do not leak by inserting /// destroys along paths that go through the intermediate phi that do not also /// go through the. void addHandOffCopyDestroysForPhis(SILInstruction *load, SILValue newVal); - - /// If as a result of us copying values, we may have unconsumed destroys, find - /// the appropriate location and place the values there. Only used when - /// ownership is enabled. - void addMissingDestroysForCopiedValues(SILInstruction *load, SILValue newVal); }; // For OptimizationMode::PreserveAlloc, insert copies at the available value's @@ -718,406 +663,6 @@ SILValue AvailableValueAggregationFixup::mergeCopies( .emitCopyValueOperation(availableAtInst->getLoc(), result); } - -namespace { - -class PhiNodeCopyCleanupInserter { - llvm::SmallMapVector incomingValues; - - /// Map from index -> (incomingValueIndex, copy). - /// - /// We are going to stable_sort this array using the indices of - /// incomingValueIndex. This will ensure that we always visit in - /// insertion order our incoming values (since the indices we are - /// sorting by are the count of incoming values we have seen so far - /// when we see the incoming value) and maintain the internal - /// insertion sort within our range as well. This ensures that we - /// visit our incoming values in visitation order and that within - /// their own values, also visit them in visitation order with - /// respect to each other. - SmallFrozenMultiMap copiesToCleanup; - - /// The lifetime frontier that we use to compute lifetime endpoints - /// when emitting cleanups. - ValueLifetimeAnalysis::Frontier lifetimeFrontier; - -public: - PhiNodeCopyCleanupInserter() = default; - - void trackNewCleanup(SILValue incomingValue, CopyValueInst *copy) { - auto entry = std::make_pair(incomingValue, incomingValues.size()); - auto iter = incomingValues.insert(entry); - // If we did not succeed, then iter.first.second is the index of - // incoming value. Otherwise, it will be nextIndex. - copiesToCleanup.insert(iter.first->second, copy); - } - - void emit(DeadEndBlocks &deadEndBlocks) &&; -}; - -} // end anonymous namespace - -static SILInstruction * -getNonPhiBlockIncomingValueDef(SILValue incomingValue, - SingleValueInstruction *phiCopy) { - assert(isa(phiCopy)); - auto *phiBlock = phiCopy->getParent(); - if (phiBlock == incomingValue->getParentBlock()) { - return nullptr; - } - - if (auto *cvi = dyn_cast(incomingValue)) { - return cvi; - } - - assert(isa(incomingValue)); - - // Otherwise, our copy_value may not be post-dominated by our phi. To - // work around that, we need to insert destroys along the other - // paths. So set base to the first instruction in our argument's block, - // so we can insert destroys for our base. - return &*incomingValue->getParentBlock()->begin(); -} - -static bool -terminatorHasAnyKnownPhis(TermInst *ti, - ArrayRef insertedPhiNodesSorted) { - for (auto succArgList : ti->getSuccessorBlockArgumentLists()) { - if (llvm::any_of(succArgList, [&](SILArgument *arg) { - return binary_search(insertedPhiNodesSorted, - cast(arg)); - })) { - return true; - } - } - - return false; -} - -void PhiNodeCopyCleanupInserter::emit(DeadEndBlocks &deadEndBlocks) && { - // READ THIS: We are being very careful here to avoid allowing for - // non-determinism to enter here. - // - // 1. First we create a list of indices of our phi node data. Then we use a - // stable sort those indices into the order in which our phi node cleanups - // would be in if we compared just using incomingValues. We use a stable - // sort here to ensure that within the same "cohort" of values, our order - // is insertion order. - // - // 2. We go through the list of phiNodeCleanupStates in insertion order. We - // also maintain a set of already visited base values. When we visit the - // first phiNodeCleanupState for a specific phi, we process the phi - // then. This ensures that we always process the phis in insertion order as - // well. - copiesToCleanup.setFrozen(); - - for (auto keyValue : copiesToCleanup.getRange()) { - unsigned incomingValueIndex = keyValue.first; - auto copies = keyValue.second; - - SILValue incomingValue = - std::next(incomingValues.begin(), incomingValueIndex)->first; - SingleValueInstruction *phiCopy = copies.front(); - auto *insertPt = getNonPhiBlockIncomingValueDef(incomingValue, phiCopy); - auto loc = RegularLocation::getAutoGeneratedLocation(); - - // Before we do anything, see if we have a single cleanup state. In such a - // case, we could have that we have a phi node as an incoming value and a - // copy_value in that same block. In such a case, we want to just insert the - // copy and continue. This means that - // cleanupState.getNonPhiBlockIncomingValueDef() should always return a - // non-null value in the code below. - if (copies.size() == 1 && isa(incomingValue) && !insertPt) { - SILBasicBlock *phiBlock = phiCopy->getParent(); - SILBuilderWithScope builder(phiBlock->getTerminator()); - builder.createDestroyValue(loc, incomingValue); - continue; - } - - // Otherwise, we know that we have for this incomingValue, multiple - // potential insert pts that we need to handle at the same time with our - // lifetime query. Lifetime extend our base over these copy_value uses. - assert(lifetimeFrontier.empty()); - auto *def = getNonPhiBlockIncomingValueDef(incomingValue, phiCopy); - assert(def && "Should never have a nullptr here since we handled all of " - "the single block cases earlier"); - ValueLifetimeAnalysis analysis(def, copies); - bool foundCriticalEdges = !analysis.computeFrontier( - lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks); - (void)foundCriticalEdges; - assert(!foundCriticalEdges); - - while (!lifetimeFrontier.empty()) { - auto *insertPoint = lifetimeFrontier.pop_back_val(); - SILBuilderWithScope builder(insertPoint); - builder.createDestroyValue(loc, incomingValue); - } - } -} - -void AvailableValueAggregationFixup::addHandOffCopyDestroysForPhis( - SILInstruction *load, SILValue newVal) { - assert(isa(load) || isa(load)); - - if (insertedPhiNodes.empty()) - return; - - SmallVector leakingBlocks; - SmallVector, 8> incomingValues; - auto loc = RegularLocation::getAutoGeneratedLocation(); - -#ifndef NDEBUG - LLVM_DEBUG(llvm::dbgs() << "Inserted Phis!\n"); - for (auto *phi : insertedPhiNodes) { - LLVM_DEBUG(llvm::dbgs() << "Phi: " << *phi); - } -#endif - - // Before we begin, identify the offset for all phis that are intermediate - // phis inserted by the SSA updater. We are taking advantage of the fact that - // the SSA updater just constructs the web without knowledge of ownership. So - // if a phi node is only used by another phi node that we inserted, then we - // have an intermediate phi node. - // - // TODO: There should be a better way of doing this than doing a copy + sort. - SmallVector insertedPhiNodesSorted; - llvm::copy(insertedPhiNodes, std::back_inserter(insertedPhiNodesSorted)); - llvm::sort(insertedPhiNodesSorted); - - SmallBitVector intermediatePhiOffsets(insertedPhiNodes.size()); - for (unsigned i : indices(insertedPhiNodes)) { - if (TermInst *termInst = - insertedPhiNodes[i]->getSingleUserOfType()) { - // Only set the value if we find termInst has a successor with a phi node - // in our insertedPhiNodes. - if (terminatorHasAnyKnownPhis(termInst, insertedPhiNodesSorted)) { - intermediatePhiOffsets.set(i); - } - } - } - - // First go through all of our phi nodes doing the following: - // - // 1. If any of the phi node have a copy_value as an operand, we know that the - // copy_value does not dominate our final definition since otherwise the - // SSA updater would not have inserted a phi node here. In such a case - // since we may not have that the copy_value is post-dominated by the phi, - // we need to insert a copy_value at the phi to allow for post-domination - // and then use the ValueLifetimeChecker to determine the rest of the - // frontier for the base value. - // - // 2. If our phi node is used by another phi node, we run into a similar - // problem where we could have that our original phi node does not dominate - // our final definition (since the SSA updater would not have inserted the - // phi) and may not be strongly control dependent on our phi. To work - // around this problem, we insert at the phi a copy_value to allow for the - // phi to post_dominate its copy and then extend the lifetime of the phied - // value over that copy. - // - // As an extra complication to this, when we insert compensating releases for - // any copy_values from (1), we need to insert the destroy_value on "base - // values" (either a copy_value or the first instruction of a phi argument's - // block) /after/ we have found all of the base_values to ensure that if the - // same base value is used by multiple phis, we do not insert too many destroy - // value. - // - // NOTE: At first glance one may think that such a problem could not occur - // with phi nodes as well. Sadly if we allow for double backedge loops, it is - // possible (there may be more cases). - PhiNodeCopyCleanupInserter cleanupInserter; - - for (unsigned i : indices(insertedPhiNodes)) { - auto *phi = insertedPhiNodes[i]; - - // If our phi is not owned, continue. No fixes are needed. - if (phi->getOwnershipKind() != OwnershipKind::Owned) - continue; - - LLVM_DEBUG(llvm::dbgs() << "Visiting inserted phi: " << *phi); - // Otherwise, we have a copy_value that may not be strongly control - // equivalent with our phi node. In such a case, we need to use - // ValueLifetimeAnalysis to lifetime extend the copy such that we can - // produce a new copy_value at the phi. We insert destroys along the - // frontier. - leakingBlocks.clear(); - incomingValues.clear(); - - phi->getIncomingPhiValues(incomingValues); - unsigned phiIndex = phi->getIndex(); - for (auto pair : incomingValues) { - SILValue value = pair.second; - - // If we had a non-trivial type with non-owned ownership, we will not see - // a copy_value, so skip them here. - if (value->getOwnershipKind() != OwnershipKind::Owned) - continue; - - // Otherwise, value should be from a copy_value or a phi node. - assert(isa(value) || isa(value)); - - // If we have a copy_value, remove it from the inserted insts set so we - // skip it when we start processing insertedInstrs. - if (auto *cvi = dyn_cast(value)) { - copyValueProcessedWithPhiNodes.insert(cvi); - - // Then check if our termInst is in the same block as our copy_value. In - // such a case, we can just use the copy_value as our phi's value - // without needing to worry about any issues around control equivalence. - if (pair.first == cvi->getParent()) - continue; - } else { - assert(isa(value)); - } - - // Otherwise, insert a copy_value instruction right before the phi. We use - // that for our actual phi. - auto *termInst = pair.first->getTerminator(); - SILBuilderWithScope builder(termInst); - CopyValueInst *phiCopy = builder.createCopyValue(loc, value); - termInst->setOperand(phiIndex, phiCopy); - - // Now that we know our base, phi, phiCopy for this specific incoming - // value, append it to the phiNodeCleanupState so we can insert - // destroy_values late after we visit all insertedPhiNodes. - cleanupInserter.trackNewCleanup(value, phiCopy); - } - - // Then see if our phi is an intermediate phi. If it is an intermediate phi, - // we know that this is not the phi node that is post-dominated by the - // load_borrow and that we will lifetime extend it via the child - // phi. Instead, we need to just ensure that our phi arg does not leak onto - // its set of post-dominating paths, subtracting from that set the path - // through our terminator use. - if (intermediatePhiOffsets[i]) { - continue; - } - - // If we reach this point, then we know that we are a phi node that actually - // dominates our user so we need to lifetime extend it over the - // load_borrow. Thus insert copy_value along the incoming edges and then - // lifetime extend the phi node over the load_borrow. - // - // The linear lifetime checker doesn't care if the passed in load is - // actually a user of our copy_value. What we care about is that the load is - // guaranteed to be in the block where we have reformed the tuple in a - // consuming manner. This means if we add it as the consuming use of the - // copy, we can find the leaking places if any exist. - // - // Then perform the linear lifetime check. If we succeed, continue. We have - // no further work to do. - auto *loadOperand = &load->getAllOperands()[0]; - LinearLifetimeChecker checker(&deadEndBlocks); - bool consumedInLoop = checker.completeConsumingUseSet( - phi, loadOperand, [&](SILBasicBlock::iterator iter) { - SILBuilderWithScope builder(iter); - builder.emitDestroyValueOperation(loc, phi); - }); - - // Ok, we found some leaking blocks and potentially that our load is - // "consumed" inside a different loop in the loop nest from cvi. If we are - // consumed in the loop, then our visit should have inserted all of the - // necessary destroys for us by inserting the destroys on the loop - // boundaries. So, continue. - // - // NOTE: This includes cases where due to an infinite loop, we did not - // insert /any/ destroys since the loop has no boundary in a certain sense. - if (consumedInLoop) { - continue; - } - - // Otherwise, we need to insert one last destroy after the load for our phi. - auto next = std::next(load->getIterator()); - SILBuilderWithScope builder(next); - builder.emitDestroyValueOperation( - RegularLocation::getAutoGeneratedLocation(), phi); - } - - // Alright! In summary, we just lifetime extended all of our phis, - // lifetime extended them to the load block, and inserted phi copies - // at all of our intermediate phi nodes. Now we need to cleanup and - // insert all of the compensating destroy_value that we need. - std::move(cleanupInserter).emit(deadEndBlocks); - - // Clear the phi node array now that we are done. - insertedPhiNodes.clear(); -} - -// TODO: use standard lifetime completion -void AvailableValueAggregationFixup::addMissingDestroysForCopiedValues( - SILInstruction *load, SILValue newVal) { - assert(load->getFunction()->hasOwnership() && - "We assume this is only called if we have ownership"); - - SmallVector leakingBlocks; - auto loc = RegularLocation::getAutoGeneratedLocation(); - - for (auto *inst : insertedInsts) { - // Otherwise, see if this is a load [copy]. It if it a load [copy], then we - // know that the load [copy] must be in the load block meaning we can just - // put a destroy_value /after/ the load_borrow to ensure that the value - // lives long enough for us to copy_value it or a derived value for the - // begin_borrow. - if (auto *li = dyn_cast(inst)) { - if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) { - assert(li->getParent() == load->getParent()); - auto next = std::next(load->getIterator()); - SILBuilderWithScope builder(next); - builder.emitDestroyValueOperation( - RegularLocation::getAutoGeneratedLocation(), li); - continue; - } - } - - // Our copy_value may have been unset above if it was used by a phi - // (implying it does not dominate our final user). - auto *cvi = dyn_cast(inst); - if (!cvi) - continue; - - // If we already handled this copy_value above when handling phi nodes, just - // continue. - if (copyValueProcessedWithPhiNodes.count(cvi)) - continue; - - // Clear our state. - leakingBlocks.clear(); - - // The linear lifetime checker doesn't care if the passed in load is - // actually a user of our copy_value. What we care about is that the load is - // guaranteed to be in the block where we have reformed the tuple in a - // consuming manner. This means if we add it as the consuming use of the - // copy, we can find the leaking places if any exist. - // - // Then perform the linear lifetime check. If we succeed, continue. We have - // no further work to do. - auto *loadOperand = &load->getAllOperands()[0]; - LinearLifetimeChecker checker(&deadEndBlocks); - bool consumedInLoop = checker.completeConsumingUseSet( - cvi, loadOperand, [&](SILBasicBlock::iterator iter) { - SILBuilderWithScope builder(iter); - builder.emitDestroyValueOperation(loc, cvi); - }); - - // Ok, we found some leaking blocks and potentially that our load is - // "consumed" inside a different loop in the loop nest from cvi. If we are - // consumed in the loop, then our visit should have inserted all of the - // necessary destroys for us by inserting the destroys on the loop - // boundaries. So, continue. - // - // NOTE: This includes cases where due to an infinite loop, we did not - // insert /any/ destroys since the loop has no boundary in a certain sense. - if (consumedInLoop) { - continue; - } - - // Otherwise, we need to insert one last destroy after the load for our phi. - auto next = std::next(load->getIterator()); - SILBuilderWithScope builder(next); - builder.emitDestroyValueOperation( - RegularLocation::getAutoGeneratedLocation(), cvi); - } -} - //===----------------------------------------------------------------------===// // Available Value Aggregation //===----------------------------------------------------------------------===// @@ -1190,15 +735,6 @@ class AvailableValueAggregator { return expectedOwnership == AvailableValueExpectedOwnership::Copy; } - /// Given a load_borrow that we have aggregated a new value for, fixup the - /// reference counts of the intermediate copies and phis to ensure that all - /// forwarding operations in the CFG are strongly control equivalent (i.e. run - /// the same number of times). - void fixupOwnership(SILInstruction *load, SILValue newVal) { - assert(isa(load) || isa(load)); - ownershipFixup.fixupOwnership(load, newVal); - } - private: SILValue aggregateFullyAvailableValue(SILType loadTy, unsigned firstElt); SILValue aggregateTupleSubElts(TupleType *tt, SILType loadTy, @@ -1535,11 +1071,6 @@ class AvailableValueDataflowContext { ownershipFixup.verifyOwnership(deBlocks); } - void fixupOwnership(InstructionDeleter &deleter, - DeadEndBlocks &deBlocks) { - ownershipFixup.fixupOwnership(deleter, deBlocks); - } - void deleteInsertedInsts(InstructionDeleter &deleter) { ownershipFixup.deleteInsertedInsts(deleter); } @@ -2257,10 +1788,6 @@ bool AvailableValueDataflowContext::hasEscapedAt(SILInstruction *I) { return HasAnyEscape; } -//===----------------------------------------------------------------------===// -// Optimize loads -//===----------------------------------------------------------------------===// - static SILType getMemoryType(AllocationInst *memory) { // Compute the type of the memory object. if (auto *abi = dyn_cast(memory)) { @@ -2274,286 +1801,6 @@ static SILType getMemoryType(AllocationInst *memory) { return cast(memory)->getElementType(); } -namespace { - -/// This performs load promotion and deletes synthesized allocations if all -/// loads can be removed. -class OptimizeAllocLoads { - - SILModule &Module; - - /// This is either an alloc_box or alloc_stack instruction. - AllocationInst *TheMemory; - - /// This is the SILType of the memory object. - SILType MemoryType; - - /// The number of primitive subelements across all elements of this memory - /// value. - unsigned NumMemorySubElements; - - SmallVectorImpl &Uses; - - InstructionDeleter &deleter; - - DeadEndBlocks &deadEndBlocks; - - /// A structure that we use to compute our available values. - AvailableValueDataflowContext DataflowContext; - -public: - OptimizeAllocLoads(AllocationInst *memory, - SmallVectorImpl &uses, - DeadEndBlocks &deadEndBlocks, - InstructionDeleter &deleter) - : Module(memory->getModule()), TheMemory(memory), - MemoryType(getMemoryType(memory)), - NumMemorySubElements(getNumSubElements( - MemoryType, *memory->getFunction(), - TypeExpansionContext(*memory->getFunction()))), - Uses(uses), deleter(deleter), deadEndBlocks(deadEndBlocks), - DataflowContext(TheMemory, NumMemorySubElements, - OptimizationMode::PreserveAlloc, uses, - deleter, deadEndBlocks) {} - - bool optimize(); - -private: - bool optimizeLoadUse(SILInstruction *inst); - bool promoteLoadCopy(LoadInst *li); - bool promoteLoadBorrow(LoadBorrowInst *lbi); - bool promoteCopyAddr(CopyAddrInst *cai); -}; - -} // end anonymous namespace - -/// If we are able to optimize \p Inst, return the source address that -/// instruction is loading from. If we can not optimize \p Inst, then just -/// return an empty SILValue. -static SILValue tryFindSrcAddrForLoad(SILInstruction *i) { - // We can always promote a load_borrow. - if (auto *lbi = dyn_cast(i)) - return lbi->getOperand(); - - // We only handle load [copy], load [trivial], load and copy_addr right - // now. Notably we do not support load [take] when promoting loads. - if (auto *li = dyn_cast(i)) - if (li->getOwnershipQualifier() != LoadOwnershipQualifier::Take) - return li->getOperand(); - - // If this is a CopyAddr, verify that the element type is loadable. If not, - // we can't explode to a load. - auto *cai = dyn_cast(i); - if (!cai || !cai->getSrc()->getType().isLoadable(*cai->getFunction())) - return SILValue(); - return cai->getSrc(); -} - -/// At this point, we know that this element satisfies the definitive init -/// requirements, so we can try to promote loads to enable SSA-based dataflow -/// analysis. We know that accesses to this element only access this element, -/// cross element accesses have been scalarized. -/// -/// This returns true if the load has been removed from the program. -bool OptimizeAllocLoads::promoteLoadCopy(LoadInst *li) { - // Note that we intentionally don't support forwarding of weak pointers, - // because the underlying value may drop be deallocated at any time. We would - // have to prove that something in this function is holding the weak value - // live across the promoted region and that isn't desired for a stable - // diagnostics pass this like one. - - // First attempt to find a source addr for our "load" instruction. If we fail - // to find a valid value, just return. - SILValue srcAddr = tryFindSrcAddrForLoad(li); - if (!srcAddr) - return false; - - SmallVector availableValues; - auto loadInfo = DataflowContext.computeAvailableValues(srcAddr, li, availableValues); - if (!loadInfo.has_value()) - return false; - - // Aggregate together all of the subelements into something that has the same - // type as the load did, and emit smaller loads for any subelements that were - // not available. We are "propagating" a +1 available value from the store - // points. - AvailableValueAggregator agg(li, availableValues, Uses, deadEndBlocks, - AvailableValueExpectedOwnership::Copy); - SILValue newVal = agg.aggregateValues(loadInfo->loadType, li->getOperand(), - loadInfo->firstElt); - - LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *li); - LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal); - ++NumLoadPromoted; - - // If we inserted any copies, we created the copies at our stores. We know - // that in our load block, we will reform the aggregate as appropriate at the - // load implying that the value /must/ be fully consumed. If we promoted a +0 - // value, we created dominating destroys along those paths. Thus any leaking - // blocks that we may have can be found by performing a linear lifetime check - // over all copies that we found using the load as the "consuming uses" (just - // for the purposes of identifying the consuming block). - agg.fixupOwnership(li, newVal); - - // Now that we have fixed up all of our missing destroys, insert the copy - // value for our actual load, in case the load was in an inner loop, and RAUW. - newVal = SILBuilderWithScope(li).emitCopyValueOperation(li->getLoc(), newVal); - - li->replaceAllUsesWith(newVal); - - SILValue addr = li->getOperand(); - deleter.forceDelete(li); - if (auto *addrI = addr->getDefiningInstruction()) - deleter.deleteIfDead(addrI); - return true; -} - -bool OptimizeAllocLoads::promoteCopyAddr(CopyAddrInst *cai) { - // Note that we intentionally don't support forwarding of weak pointers, - // because the underlying value may drop be deallocated at any time. We would - // have to prove that something in this function is holding the weak value - // live across the promoted region and that isn't desired for a stable - // diagnostics pass this like one. - - // First attempt to find a source addr for our "load" instruction. If we fail - // to find a valid value, just return. - SILValue srcAddr = tryFindSrcAddrForLoad(cai); - if (!srcAddr) - return false; - - SmallVector availableValues; - auto result = DataflowContext.computeAvailableValues(srcAddr, cai, - availableValues); - if (!result.has_value()) - return false; - - // Ok, we have some available values. If we have a copy_addr, explode it now, - // exposing the load operation within it. Subsequent optimization passes will - // see the load and propagate the available values into it. - DataflowContext.explodeCopyAddr(cai); - - // This is removing the copy_addr, but explodeCopyAddr takes care of - // removing the instruction from Uses for us, so we return false. - return false; -} - -/// At this point, we know that this element satisfies the definitive init -/// requirements, so we can try to promote loads to enable SSA-based dataflow -/// analysis. We know that accesses to this element only access this element, -/// cross element accesses have been scalarized. -/// -/// This returns true if the load has been removed from the program. -bool OptimizeAllocLoads::promoteLoadBorrow(LoadBorrowInst *lbi) { - // Note that we intentionally don't support forwarding of weak pointers, - // because the underlying value may drop be deallocated at any time. We would - // have to prove that something in this function is holding the weak value - // live across the promoted region and that isn't desired for a stable - // diagnostics pass this like one. - - // First attempt to find a source addr for our "load" instruction. If we fail - // to find a valid value, just return. - SILValue srcAddr = tryFindSrcAddrForLoad(lbi); - if (!srcAddr) - return false; - - SmallVector availableValues; - auto loadInfo = DataflowContext.computeAvailableValues(srcAddr, lbi, - availableValues); - if (!loadInfo.has_value()) - return false; - - // Bail if the load_borrow has reborrows. In this case it's not so easy to - // find the insertion points for the destroys. - if (!lbi->getUsersOfType().empty()) { - return false; - } - - ++NumLoadPromoted; - - // Aggregate together all of the subelements into something that has the same - // type as the load did, and emit smaller loads for any subelements that were - // not available. We are "propagating" a +1 available value from the store - // points. - AvailableValueAggregator agg(lbi, availableValues, Uses, deadEndBlocks, - AvailableValueExpectedOwnership::Borrow); - SILValue newVal = agg.aggregateValues(loadInfo->loadType, lbi->getOperand(), - loadInfo->firstElt); - - LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *lbi); - LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal); - - // If we inserted any copies, we created the copies at our - // stores. We know that in our load block, we will reform the - // aggregate as appropriate, will borrow the value there and give us - // a whole pristine new value. Now in this routine, we go through - // all of the copies and phis that we inserted and ensure that: - // - // 1. Phis are always strongly control equivalent to the copies that - // produced their incoming values. - // - // 2. All intermediate copies are properly lifetime extended to the - // load block and all leaking blocks are filled in as appropriate - // with destroy_values. - agg.fixupOwnership(lbi, newVal); - - // Now that we have fixed up the lifetimes of all of our incoming copies so - // that they are alive over the load point, copy, borrow newVal and insert - // destroy_value after the end_borrow and then RAUW. - SILBuilderWithScope builder(lbi); - SILValue copiedVal = builder.emitCopyValueOperation(lbi->getLoc(), newVal); - newVal = builder.createBeginBorrow(lbi->getLoc(), copiedVal); - - for (auto *ebi : lbi->getUsersOfType()) { - auto next = std::next(ebi->getIterator()); - SILBuilderWithScope(next).emitDestroyValueOperation(ebi->getLoc(), - copiedVal); - } - - lbi->replaceAllUsesWith(newVal); - - SILValue addr = lbi->getOperand(); - deleter.forceDelete(lbi); - if (auto *addrI = addr->getDefiningInstruction()) - deleter.deleteIfDead(addrI); - return true; -} - -bool OptimizeAllocLoads::optimize() { - bool changed = false; - - // If we've successfully checked all of the definitive initialization - // requirements, try to promote loads. This can explode copy_addrs, so the - // use list may change size. - for (unsigned i = 0; i != Uses.size(); ++i) { - auto &use = Uses[i]; - // Ignore entries for instructions that got expanded along the way. - if (use.Inst && use.Kind == PMOUseKind::Load) { - if (optimizeLoadUse(use.Inst)) { - changed = true; - Uses[i].Inst = nullptr; // remove entry if load got deleted. - } - } - } - return changed; -} - -bool OptimizeAllocLoads::optimizeLoadUse(SILInstruction *inst) { - // After replacing load uses with promoted values, fixup ownership for copies - // or casts inserted during dataflow. - SWIFT_DEFER { DataflowContext.fixupOwnership(deleter, deadEndBlocks); }; - - if (auto *cai = dyn_cast(inst)) - return promoteCopyAddr(cai); - - if (auto *lbi = dyn_cast(inst)) - return promoteLoadBorrow(lbi); - - if (auto *li = dyn_cast(inst)) - return promoteLoadCopy(li); - - return false; -} - //===----------------------------------------------------------------------===// // Optimize dead allocation: // Fully promote each access @@ -3180,49 +2427,6 @@ static AllocationInst *getOptimizableAllocation(SILInstruction *i) { return alloc; } -bool swift::optimizeMemoryAccesses(SILFunction *fn) { - if (!fn->hasOwnership()) { - return false; - } - - bool changed = false; - DeadEndBlocks deadEndBlocks(fn); - InstructionDeleter deleter; - for (auto &bb : *fn) { - for (SILInstruction &inst : bb.deletableInstructions()) { - // First see if i is an allocation that we can optimize. If not, skip it. - AllocationInst *alloc = getOptimizableAllocation(&inst); - if (!alloc) { - continue; - } - - LLVM_DEBUG(llvm::dbgs() - << "*** PMO Optimize Memory Accesses looking at: " << *alloc); - PMOMemoryObjectInfo memInfo(alloc); - - // Set up the datastructure used to collect the uses of the allocation. - SmallVector uses; - - // Walk the use list of the pointer, collecting them. If we are not able - // to optimize, skip this value. *NOTE* We may still scalarize values - // inside the value. - if (!collectPMOElementUsesFrom(memInfo, uses)) { - // Avoid advancing this iterator until after collectPMOElementUsesFrom() - // runs. It creates and deletes instructions other than alloc. - continue; - } - OptimizeAllocLoads optimizeAllocLoads(alloc, uses, deadEndBlocks, - deleter); - changed |= optimizeAllocLoads.optimize(); - - // Move onto the next instruction. We know this is safe since we do not - // eliminate allocations here. - } - } - - return changed; -} - bool swift::eliminateDeadAllocations(SILFunction *fn, DominanceInfo *domInfo) { if (!fn->hasOwnership()) { return false; @@ -3268,26 +2472,6 @@ bool swift::eliminateDeadAllocations(SILFunction *fn, DominanceInfo *domInfo) { namespace { -class PredictableMemoryAccessOptimizations : public SILFunctionTransform { - /// The entry point to the transformation. - /// - /// FIXME: This pass should not need to rerun on deserialized - /// functions. Nothing should have changed in the upstream pipeline after - /// deserialization. However, rerunning does improve some benchmarks. This - /// either indicates that this pass missing some opportunities the first time, - /// or has a pass order dependency on other early passes. - void run() override { - auto *func = getFunction(); - if (!func->hasOwnership()) - return; - - LLVM_DEBUG(llvm::dbgs() << "Looking at: " << func->getName() << "\n"); - // TODO: Can we invalidate here just instructions? - if (optimizeMemoryAccesses(func)) - invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); - } -}; - class PredictableDeadAllocationElimination : public SILFunctionTransform { void run() override { auto *func = getFunction(); @@ -3306,10 +2490,6 @@ class PredictableDeadAllocationElimination : public SILFunctionTransform { } // end anonymous namespace -SILTransform *swift::createPredictableMemoryAccessOptimizations() { - return new PredictableMemoryAccessOptimizations(); -} - SILTransform *swift::createPredictableDeadAllocationElimination() { return new PredictableDeadAllocationElimination(); } From d351d10845a9c542b423a9ad428658d8b0fa217b Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 7 Feb 2025 11:26:39 +0100 Subject: [PATCH 8/8] tests: add a testfile for a predictable-memory-opt complexity problem. Reported here: https://github.com/swiftlang/swift/issues/56221 rdar://72885279 --- .../SILOptimizer/large_c_struct.swift | 1310 +++++++++++++++++ 1 file changed, 1310 insertions(+) create mode 100644 validation-test/SILOptimizer/large_c_struct.swift diff --git a/validation-test/SILOptimizer/large_c_struct.swift b/validation-test/SILOptimizer/large_c_struct.swift new file mode 100644 index 0000000000000..51a4c22e6bf56 --- /dev/null +++ b/validation-test/SILOptimizer/large_c_struct.swift @@ -0,0 +1,1310 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// The compiler should finish in less than 20 seconds. To give some slack, +// specify a timeout of 1 minute. +// If the compiler needs more than 1 minute, there is probably a real problem. +// So please don't just increase the timeout in case this fails. + +// RUN: %{python} %S/../../test/Inputs/timeout.py 60 %target-swift-frontend -c -o %t/out.o -primary-file %t/test.swift -import-objc-header %t/bigstruct.h + +// REQUIRES: long_test + +//--- test.swift + +func createTestABigStruct1() -> test_a_big_struct_1 { + var testABigStruct1 = test_a_big_struct_1() + + testABigStruct1.var1 = 0 + + return testABigStruct1 +} + +func createTestABigStruct2() -> test_a_big_struct_2 { + var testABigStruct2 = test_a_big_struct_2() + + testABigStruct2.var1 = 0 + + return testABigStruct2 +} + +var testABigStruct1 = createTestABigStruct1() +var testABigStruct2 = createTestABigStruct2() + +test_a_print(&testABigStruct1, &testABigStruct2) + +//--- bigstruct.h + +#define TEST_A_SIZE_BIG 1024 +#define TEST_A_SIZE_SMALL 8 + +typedef struct +{ + int var1; + int var2; + int var3; +} +test_a_other_struct_1; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_2; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_3; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_4; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_5; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_6; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_7; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_8; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_9; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_10; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_11; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_12; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_13; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_14; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_15; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_16; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_17; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_18; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_19; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_20; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_21; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_22; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_23; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_24; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_25; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_26; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_27; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_28; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_29; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_30; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_31; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_32; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_33; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_34; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_35; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_36; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_37; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_38; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_39; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_40; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_41; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_42; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_43; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_44; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_45; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_46; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_47; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_48; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_49; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_50; + +typedef struct +{ + int var1; + int var2; + int var3; + int var4; + int var5; + int var6; + int var7; + int var8; + int var9; + int var10; +} +test_a_other_struct_51; + +typedef struct +{ + int var1; + int arr1_2[TEST_A_SIZE_BIG]; + int arr1_3[TEST_A_SIZE_BIG]; + int arr1_4[TEST_A_SIZE_BIG]; + int arr1_5[TEST_A_SIZE_BIG]; + int arr1_6[TEST_A_SIZE_BIG]; + int arr1_7[TEST_A_SIZE_BIG]; + int arr1_8[TEST_A_SIZE_BIG]; + int arr1_9[TEST_A_SIZE_BIG]; + int arr1_10[TEST_A_SIZE_BIG]; + int arr1_11[TEST_A_SIZE_BIG]; + int arr1_12[TEST_A_SIZE_BIG]; + int arr1_13[TEST_A_SIZE_BIG]; + int arr1_14[TEST_A_SIZE_BIG]; + int arr1_15[TEST_A_SIZE_BIG]; + int arr1_16[TEST_A_SIZE_BIG]; + int arr1_17[TEST_A_SIZE_BIG]; + int arr1_18[TEST_A_SIZE_BIG]; + int arr1_19[TEST_A_SIZE_BIG]; + int arr1_20[TEST_A_SIZE_BIG]; + int arr1_21[TEST_A_SIZE_BIG]; + int arr1_22[TEST_A_SIZE_BIG]; + int arr1_23[TEST_A_SIZE_BIG]; + int arr1_24[TEST_A_SIZE_BIG]; + int arr1_25[TEST_A_SIZE_BIG]; + int arr1_26[TEST_A_SIZE_BIG]; + int arr1_27[TEST_A_SIZE_BIG]; + int arr1_28[TEST_A_SIZE_BIG]; + int arr1_29[TEST_A_SIZE_BIG]; + int arr1_30[TEST_A_SIZE_BIG]; + int arr1_31[TEST_A_SIZE_BIG]; + int arr1_32[TEST_A_SIZE_BIG]; + int arr1_33[TEST_A_SIZE_BIG]; + int arr1_34[TEST_A_SIZE_BIG]; + int arr1_35[TEST_A_SIZE_BIG]; + int arr1_36[TEST_A_SIZE_BIG]; + int arr1_37[TEST_A_SIZE_BIG]; + int arr1_38[TEST_A_SIZE_BIG]; + int arr1_39[TEST_A_SIZE_BIG]; + int arr1_40[TEST_A_SIZE_BIG]; + int arr1_41[TEST_A_SIZE_BIG]; + int arr1_42[TEST_A_SIZE_BIG]; + int arr1_43[TEST_A_SIZE_BIG]; + int arr1_44[TEST_A_SIZE_BIG]; + int arr1_45[TEST_A_SIZE_BIG]; + int arr1_46[TEST_A_SIZE_BIG]; + int arr1_47[TEST_A_SIZE_BIG]; + int arr1_48[TEST_A_SIZE_BIG]; + int arr1_49[TEST_A_SIZE_BIG]; + int arr1_50[TEST_A_SIZE_BIG]; + int arr1_51[TEST_A_SIZE_BIG]; + + int var2; + test_a_other_struct_2 arr2[TEST_A_SIZE_BIG]; + + int var3; + test_a_other_struct_3 arr3[TEST_A_SIZE_BIG]; + + int var4; + test_a_other_struct_4 arr4[TEST_A_SIZE_BIG]; + + int var5; + test_a_other_struct_5 arr5[TEST_A_SIZE_BIG]; + + int var6; + test_a_other_struct_6 arr6[TEST_A_SIZE_BIG]; + + int var7; + test_a_other_struct_7 arr7[TEST_A_SIZE_BIG]; + + int var8; + test_a_other_struct_8 arr8[TEST_A_SIZE_BIG]; + + int var9; + test_a_other_struct_9 arr9[TEST_A_SIZE_BIG]; + + int var10; + test_a_other_struct_10 arr10[TEST_A_SIZE_BIG]; + + int var11; + test_a_other_struct_11 arr11[TEST_A_SIZE_BIG]; + + int var12; + test_a_other_struct_12 arr12[TEST_A_SIZE_BIG]; + + int var13; + test_a_other_struct_13 arr13[TEST_A_SIZE_BIG]; + + int var14; + test_a_other_struct_14 arr14[TEST_A_SIZE_BIG]; + + int var15; + test_a_other_struct_15 arr15[TEST_A_SIZE_BIG]; + + int var16; + test_a_other_struct_16 arr16[TEST_A_SIZE_BIG]; + + int var17; + test_a_other_struct_17 arr17[TEST_A_SIZE_BIG]; + + int var18; + test_a_other_struct_18 arr18[TEST_A_SIZE_BIG]; + + int var19; + test_a_other_struct_19 arr19[TEST_A_SIZE_BIG]; + + int var20; + test_a_other_struct_20 arr20[TEST_A_SIZE_BIG]; + + int var21; + test_a_other_struct_21 arr21[TEST_A_SIZE_BIG]; + + int var22; + test_a_other_struct_22 arr22[TEST_A_SIZE_BIG]; + + int var23; + test_a_other_struct_23 arr23[TEST_A_SIZE_BIG]; + + int var24; + test_a_other_struct_24 arr24[TEST_A_SIZE_BIG]; + + int var25; + test_a_other_struct_25 arr25[TEST_A_SIZE_BIG]; + + int var26; + test_a_other_struct_26 arr26[TEST_A_SIZE_BIG]; + + int var27; + test_a_other_struct_27 arr27[TEST_A_SIZE_BIG]; + + int var28; + test_a_other_struct_28 arr28[TEST_A_SIZE_BIG]; + + int var29; + test_a_other_struct_29 arr29[TEST_A_SIZE_BIG]; + + int var30; + test_a_other_struct_30 arr30[TEST_A_SIZE_BIG]; + + int var31; + test_a_other_struct_31 arr31[TEST_A_SIZE_BIG]; + + int var32; + test_a_other_struct_32 arr32[TEST_A_SIZE_BIG]; + + int var33; + test_a_other_struct_33 arr33[TEST_A_SIZE_BIG]; + + int var34; + test_a_other_struct_34 arr34[TEST_A_SIZE_BIG]; + + int var35; + test_a_other_struct_35 arr35[TEST_A_SIZE_BIG]; + + int var36; + test_a_other_struct_36 arr36[TEST_A_SIZE_BIG]; + + int var37; + test_a_other_struct_37 arr37[TEST_A_SIZE_BIG]; + + int var38; + test_a_other_struct_38 arr38[TEST_A_SIZE_BIG]; + + int var39; + test_a_other_struct_39 arr39[TEST_A_SIZE_BIG]; + + int var40; + test_a_other_struct_40 arr40[TEST_A_SIZE_BIG]; + + int var41; + test_a_other_struct_41 arr41[TEST_A_SIZE_BIG]; + + int var42; + test_a_other_struct_42 arr42[TEST_A_SIZE_BIG]; + + int var43; + test_a_other_struct_43 arr43[TEST_A_SIZE_BIG]; + + int var44; + test_a_other_struct_44 arr44[TEST_A_SIZE_BIG]; + + int var45; + test_a_other_struct_45 arr45[TEST_A_SIZE_BIG]; + + int var46; + test_a_other_struct_46 arr46[TEST_A_SIZE_BIG]; + + int var47; + test_a_other_struct_47 arr47[TEST_A_SIZE_BIG]; + + int var48; + test_a_other_struct_48 arr48[TEST_A_SIZE_BIG]; + + int var49; + test_a_other_struct_49 arr49[TEST_A_SIZE_BIG]; + + int var50; + test_a_other_struct_50 arr50[TEST_A_SIZE_BIG]; + + int var51; + test_a_other_struct_51 arr51[TEST_A_SIZE_BIG]; +} +test_a_big_struct_1; + +typedef struct +{ + int var1; + int arr1_1[TEST_A_SIZE_SMALL]; + int arr1_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr1_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr1_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var2; + int arr2_1[TEST_A_SIZE_SMALL]; + int arr2_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr2_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr2_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var3; + int arr3_1[TEST_A_SIZE_SMALL]; + int arr3_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr3_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr3_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var4; + int arr4_1[TEST_A_SIZE_SMALL]; + int arr4_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr4_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr4_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var5; + int arr5_1[TEST_A_SIZE_SMALL]; + int arr5_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr5_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr5_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var6; + int arr6_1[TEST_A_SIZE_SMALL]; + int arr6_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr6_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr6_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var7; + int arr7_1[TEST_A_SIZE_SMALL]; + int arr7_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr7_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr7_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var8; + int arr8_1[TEST_A_SIZE_SMALL]; + int arr8_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr8_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr8_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var9; + int arr9_1[TEST_A_SIZE_SMALL]; + int arr9_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr9_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr9_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var10; + int arr10_1[TEST_A_SIZE_SMALL]; + int arr10_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr10_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr10_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var11; + int arr11_1[TEST_A_SIZE_SMALL]; + int arr11_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr11_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr11_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var12; + int arr12_1[TEST_A_SIZE_SMALL]; + int arr12_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr12_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr12_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var13; + int arr13_1[TEST_A_SIZE_SMALL]; + int arr13_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr13_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr13_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var14; + int arr14_1[TEST_A_SIZE_SMALL]; + int arr14_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr14_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr14_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var15; + int arr15_1[TEST_A_SIZE_SMALL]; + int arr15_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr15_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr15_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var16; + int arr16_1[TEST_A_SIZE_SMALL]; + int arr16_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr16_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr16_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var17; + int arr17_1[TEST_A_SIZE_SMALL]; + int arr17_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr17_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr17_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var18; + int arr18_1[TEST_A_SIZE_SMALL]; + int arr18_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr18_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr18_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var19; + int arr19_1[TEST_A_SIZE_SMALL]; + int arr19_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr19_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr19_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var20; + int arr20_1[TEST_A_SIZE_SMALL]; + int arr20_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr20_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr20_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var21; + int arr21_1[TEST_A_SIZE_SMALL]; + int arr21_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr21_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr21_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var22; + int arr22_1[TEST_A_SIZE_SMALL]; + int arr22_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr22_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr22_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var23; + int arr23_1[TEST_A_SIZE_SMALL]; + int arr23_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr23_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr23_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var24; + int arr24_1[TEST_A_SIZE_SMALL]; + int arr24_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr24_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr24_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var25; + int arr25_1[TEST_A_SIZE_SMALL]; + int arr25_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr25_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr25_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var26; + int arr26_1[TEST_A_SIZE_SMALL]; + int arr26_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr26_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr26_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var27; + int arr27_1[TEST_A_SIZE_SMALL]; + int arr27_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr27_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr27_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var28; + int arr28_1[TEST_A_SIZE_SMALL]; + int arr28_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr28_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr28_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var29; + int arr29_1[TEST_A_SIZE_SMALL]; + int arr29_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr29_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr29_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var30; + int arr30_1[TEST_A_SIZE_SMALL]; + int arr30_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr30_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr30_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var31; + int arr31_1[TEST_A_SIZE_SMALL]; + int arr31_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr31_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr31_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var32; + int arr32_1[TEST_A_SIZE_SMALL]; + int arr32_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr32_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr32_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var33; + int arr33_1[TEST_A_SIZE_SMALL]; + int arr33_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr33_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr33_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var34; + int arr34_1[TEST_A_SIZE_SMALL]; + int arr34_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr34_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr34_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var35; + int arr35_1[TEST_A_SIZE_SMALL]; + int arr35_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr35_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr35_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var36; + int arr36_1[TEST_A_SIZE_SMALL]; + int arr36_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr36_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr36_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var37; + int arr37_1[TEST_A_SIZE_SMALL]; + int arr37_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr37_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr37_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var38; + int arr38_1[TEST_A_SIZE_SMALL]; + int arr38_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr38_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr38_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var39; + int arr39_1[TEST_A_SIZE_SMALL]; + int arr39_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr39_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr39_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var40; + int arr40_1[TEST_A_SIZE_SMALL]; + int arr40_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr40_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr40_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var41; + int arr41_1[TEST_A_SIZE_SMALL]; + int arr41_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr41_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr41_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var42; + int arr42_1[TEST_A_SIZE_SMALL]; + int arr42_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr42_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr42_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var43; + int arr43_1[TEST_A_SIZE_SMALL]; + int arr43_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr43_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr43_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var44; + int arr44_1[TEST_A_SIZE_SMALL]; + int arr44_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr44_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr44_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var45; + int arr45_1[TEST_A_SIZE_SMALL]; + int arr45_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr45_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr45_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var46; + int arr46_1[TEST_A_SIZE_SMALL]; + int arr46_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr46_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr46_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var47; + int arr47_1[TEST_A_SIZE_SMALL]; + int arr47_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr47_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr47_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var48; + int arr48_1[TEST_A_SIZE_SMALL]; + int arr48_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr48_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr48_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var49; + int arr49_1[TEST_A_SIZE_SMALL]; + int arr49_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr49_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr49_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + + int var50; + int arr50_1[TEST_A_SIZE_SMALL]; + int arr50_2[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; + int arr50_3[TEST_A_SIZE_SMALL]; + test_a_other_struct_1 arr50_4[TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL][TEST_A_SIZE_SMALL]; +} +test_a_big_struct_2; + +void test_a_print(test_a_big_struct_1 *big_struct_1, test_a_big_struct_2 *big_struct_2); +