diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift index d92d7983bbee5..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]) @@ -251,7 +289,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) @@ -279,6 +317,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 +348,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: /// @@ -342,6 +417,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 +457,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 +466,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 +496,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 + } } } } @@ -479,7 +575,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 @@ -520,6 +616,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 +712,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/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/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 05be31e863f76..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 { @@ -863,6 +867,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/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); diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 5ff791ae197ad..66cce0ec1b074 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", @@ -351,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(); } 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/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) -> () 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 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..008fe6d079d2c 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) -> () @@ -1264,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) { @@ -1396,6 +1436,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]] @@ -1531,3 +1589,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 +} + 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); +