diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift index 3e00c4eea75ab..c511430d3dd64 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift @@ -53,27 +53,37 @@ let copyToBorrowOptimization = FunctionPass(name: "copy-to-borrow-optimization") return } + var changed = false + for inst in function.instructions { switch inst { case let load as LoadInst: - optimize(load: load, context) + if optimize(load: load, context) { + changed = true + } case let copy as CopyValueInst: - optimize(copy: copy, context) + if optimize(copy: copy, context) { + changed = true + } default: break } } + + if changed { + updateBorrowedFrom(in: function, context) + } } -private func optimize(load: LoadInst, _ context: FunctionPassContext) { +private func optimize(load: LoadInst, _ context: FunctionPassContext) -> Bool { if load.loadOwnership != .copy { - return + return false } var collectedUses = Uses(context) defer { collectedUses.deinitialize() } if !collectedUses.collectUses(of: load) { - return + return false } if mayWrite(toAddressOf: load, @@ -81,21 +91,22 @@ private func optimize(load: LoadInst, _ context: FunctionPassContext) { usersInDeadEndBlocks: collectedUses.usersInDeadEndBlocks, context) { - return + return false } load.replaceWithLoadBorrow(collectedUses: collectedUses) + return true } -private func optimize(copy: CopyValueInst, _ context: FunctionPassContext) { +private func optimize(copy: CopyValueInst, _ context: FunctionPassContext) -> Bool { if copy.fromValue.ownership != .guaranteed { - return + return false } var collectedUses = Uses(context) defer { collectedUses.deinitialize() } if !collectedUses.collectUses(of: copy) { - return + return false } var liverange = InstructionRange(begin: copy, context) @@ -104,10 +115,11 @@ private func optimize(copy: CopyValueInst, _ context: FunctionPassContext) { liverange.insert(contentsOf: collectedUses.usersInDeadEndBlocks) if !liverange.isFullyContainedIn(borrowScopeOf: copy.fromValue.lookThroughForwardingInstructions) { - return + return false } remove(copy: copy, collectedUses: collectedUses, liverange: liverange) + return true } private struct Uses { diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt index 02a9a02e01f90..a2b53a5fdd3a7 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt @@ -10,7 +10,7 @@ swift_compiler_sources(Optimizer SimplifyAllocRefDynamic.swift SimplifyAllocStack.swift SimplifyApply.swift - SimplifyBeginAndLoadBorrow.swift + SimplifyBeginBorrow.swift SimplifyBeginCOWMutation.swift SimplifyBranch.swift SimplifyBuiltin.swift @@ -31,8 +31,8 @@ swift_compiler_sources(Optimizer SimplifyInitEnumDataAddr.swift SimplifyKeyPath.swift SimplifyLoad.swift + SimplifyLoadBorrow.swift SimplifyMarkDependence.swift - SimplifyMisc.swift SimplifyPartialApply.swift SimplifyPointerToAddress.swift SimplifyRefCasts.swift @@ -43,6 +43,7 @@ swift_compiler_sources(Optimizer SimplifySwitchEnum.swift SimplifyTuple.swift SimplifyTupleExtract.swift + SimplifyTypeValue.swift SimplifyUncheckedAddrCast.swift SimplifyUncheckedEnumData.swift SimplifyValueToBridgeObject.swift diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginAndLoadBorrow.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginBorrow.swift similarity index 74% rename from SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginAndLoadBorrow.swift rename to SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginBorrow.swift index ddf5d8860760d..7dcbf2776e740 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginAndLoadBorrow.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginBorrow.swift @@ -1,4 +1,4 @@ -//===--- SimplifyBeginAndLoadBorrow.swift ---------------------------------===// +//===--- SimplifyBeginBorrow.swift ----------------------------------------===// // // This source file is part of the Swift.org open source project // @@ -14,68 +14,72 @@ import SIL extension BeginBorrowInst : OnoneSimplifiable, SILCombineSimplifiable { func simplify(_ context: SimplifyContext) { - if borrowedValue.ownership == .owned, - // We need to keep lexical lifetimes in place. - !isLexical, - // The same for borrow-scopes which encapsulated pointer escapes. - !findPointerEscapingUse(of: borrowedValue) - { - tryReplaceBorrowWithOwnedOperand(beginBorrow: self, context) - } else { - removeBorrowOfThinFunction(beginBorrow: self, context) + if isLexical && context.preserveDebugInfo { + // We must not remove `begin_borrow [lexical] because this is important for diagnostic passes. + return } - } -} -extension LoadBorrowInst : Simplifiable, SILCombineSimplifiable { - func simplify(_ context: SimplifyContext) { - if uses.ignoreDebugUses.ignore(usersOfType: EndBorrowInst.self).isEmpty { - context.erase(instructionIncludingAllUsers: self) - return + switch borrowedValue.ownership { + case .owned: + if tryReplaceBorrowWithOwnedOperand(beginBorrow: self, context) { + return + } + case .guaranteed: + if tryReplaceInnerBorrowScope(beginBorrow: self, context) { + return + } + default: + // Note that the operand of `begin_borrow` can have "none" ownership, e.g. in case of + // ``` + // %1 = enum $NonTrivialEnum, #NonTrivialEnum.trivialCase!enumelt // ownership = none + // %2 = begin_borrow %1 + // ``` + break } + removeBorrowOfThinFunction(beginBorrow: self, context) + } +} - // If the load_borrow is followed by a copy_value, combine both into a `load [copy]`: - // ``` - // %1 = load_borrow %0 - // %2 = some_forwarding_instruction %1 // zero or more forwarding instructions - // %3 = copy_value %2 - // end_borrow %1 - // ``` - // -> - // ``` - // %1 = load [copy] %0 - // %3 = some_forwarding_instruction %1 // zero or more forwarding instructions - // ``` - // - tryCombineWithCopy(context) +// See comments of `tryReplaceCopy` and `convertAllUsesToOwned` +private func tryReplaceBorrowWithOwnedOperand(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) -> Bool { + if findPointerEscapingUse(of: beginBorrow.borrowedValue) { + return false } - private func tryCombineWithCopy(_ context: SimplifyContext) { - let forwardedValue = lookThroughSingleForwardingUses() - guard let singleUser = forwardedValue.uses.ignore(usersOfType: EndBorrowInst.self).singleUse?.instruction, - let copy = singleUser as? CopyValueInst, - copy.parentBlock == self.parentBlock else { - return - } - let builder = Builder(before: self, context) - let loadCopy = builder.createLoad(fromAddress: address, ownership: .copy) - let forwardedOwnedValue = replaceGuaranteed(value: self, withOwnedValue: loadCopy, context) - copy.replace(with: forwardedOwnedValue, context) - context.erase(instructionIncludingAllUsers: self) + // The last value of a (potentially empty) forwarding chain, beginning at the `begin_borrow`. + let forwardedValue = beginBorrow.lookThroughOwnedConvertibaleForwardingChain() + guard forwardedValue.allUsesCanBeConvertedToOwned else { + return false } + if tryReplaceCopy(of: forwardedValue, withCopiedOperandOf: beginBorrow, context) { + return true + } + if beginBorrow.borrowedValue.isDestroyed(after: beginBorrow) { + convertAllUsesToOwned(of: beginBorrow, context) + return true + } + return false } -private func tryReplaceBorrowWithOwnedOperand(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) { - // The last value of a (potentially empty) forwarding chain, beginning at the `begin_borrow`. - let forwardedValue = beginBorrow.lookThroughSingleForwardingUses() - if forwardedValue.allUsesCanBeConvertedToOwned { - if tryReplaceCopy(of: forwardedValue, withCopiedOperandOf: beginBorrow, context) { - return - } - if beginBorrow.borrowedValue.isDestroyed(after: beginBorrow) { - convertAllUsesToOwned(of: beginBorrow, context) - } +/// Removes a borrow scope if the borrowed operand is already a guaranteed value. +/// ``` +/// bb0(%0 : @guaranteed $T): +/// %1 = begin_borrow %0 +/// // ... uses of %1 +/// end_borrow %1 +/// ``` +/// -> +/// ``` +/// bb0(%0 : @guaranteed $T): +/// // ... uses of %0 +/// ``` +private func tryReplaceInnerBorrowScope(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) -> Bool { + guard beginBorrow.scopeEndingOperands.allSatisfy({ $0.instruction is EndBorrowInst }) else { + return false } + beginBorrow.uses.ignore(usersOfType: EndBorrowInst.self).replaceAll(with: beginBorrow.borrowedValue, context) + context.erase(instructionIncludingAllUsers: beginBorrow) + return true } private func removeBorrowOfThinFunction(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) { @@ -143,8 +147,9 @@ private func convertAllUsesToOwned(of beginBorrow: BeginBorrowInst, _ context: S context.erase(instructionIncludingAllUsers: beginBorrow) } -private extension Value { - /// Returns the last value of a (potentially empty) forwarding chain. +extension Value { + /// Returns the last value of a (potentially empty) forwarding chain where all operands can be + /// converted to "owned" ownership. /// For example, returns %3 for the following def-use chain: /// ``` /// %1 = struct_extract %self, #someField @@ -152,18 +157,20 @@ private extension Value { /// %3 = struct $S(%2) // %3 has no forwarding users /// ``` /// Returns self if this value has no uses which are ForwardingInstructions. - func lookThroughSingleForwardingUses() -> Value { - if let singleUse = uses.ignore(usersOfType: EndBorrowInst.self).singleUse, + func lookThroughOwnedConvertibaleForwardingChain() -> Value { + if let singleUse = uses.ignore(usersOfType: EndBorrowInst.self).ignoreDebugUses.ignoreTypeDependence.singleUse, let fwdInst = singleUse.instruction as? (SingleValueInstruction & ForwardingInstruction), fwdInst.canConvertToOwned, fwdInst.isSingleForwardedOperand(singleUse), fwdInst.parentBlock == parentBlock { - return fwdInst.lookThroughSingleForwardingUses() + return fwdInst.lookThroughOwnedConvertibaleForwardingChain() } return self } +} +private extension Value { var allUsesCanBeConvertedToOwned: Bool { let relevantUses = uses.ignore(usersOfType: EndBorrowInst.self) return relevantUses.allSatisfy { $0.canAccept(ownership: .owned) } @@ -209,12 +216,12 @@ private extension ForwardingInstruction { /// Replaces a guaranteed value with an owned value. /// -/// If the `guaranteedValue`'s use is a ForwardingInstruction (or forwarding instruction chain), +/// If the `value`'s use is a ForwardingInstruction (or forwarding instruction chain), /// it is converted to an owned version of the forwarding instruction (or instruction chain). /// -/// Returns the last owned value in a forwarding-chain or `ownedValue` if `guaranteedValue` has +/// Returns the last owned value in a forwarding-chain or `ownedValue` if `value` has /// no forwarding uses. -private func replaceGuaranteed(value: Value, withOwnedValue ownedValue: Value, _ context: SimplifyContext) -> Value { +func replaceGuaranteed(value: SingleValueInstruction, withOwnedValue ownedValue: Value, _ context: SimplifyContext) -> Value { var result = ownedValue var numForwardingUses = 0 for use in value.uses { @@ -239,6 +246,11 @@ private func replaceGuaranteed(value: Value, withOwnedValue ownedValue: Value, _ result = replaceGuaranteed(value: fwdInst, withOwnedValue: fwdInst, context) case is EndBorrowInst: break + case let dv as DebugValueInst where dv != value.next: + // Move the debug_value immediatly after the value definition to avoid a use-after-consume + // in case the debug_value is originally located after the forwarding instruction. + dv.move(before: value.next!, context) + fallthrough default: precondition(use.canAccept(ownership: .owned)) use.set(to: ownedValue, context) diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyDestructure.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyDestructure.swift index b87cfbf69d7d1..3876d619fe27c 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyDestructure.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyDestructure.swift @@ -50,19 +50,6 @@ private extension DestructureInstruction { func foldWithAggregateConstruction(_ context: SimplifyContext) { - if aggregate.type.isTrivial(in: parentFunction) { - // ``` - // (%1, %2) = destructure_tuple %t - // ``` - // -> - // ``` - // %1 = tuple_extract %t, 0 - // %2 = tuple_extract %t, 1 - // ``` - replaceWithAggregateExtract(context) - return - } - switch aggregate { case let constructInst as ConstructureInstruction: // Eliminate the redundant instruction pair @@ -99,6 +86,21 @@ private extension DestructureInstruction { default: break } + + if !isDeleted, + aggregate.type.isTrivial(in: parentFunction) || aggregate.ownership == .guaranteed + { + // ``` + // (%1, %2) = destructure_tuple %t + // ``` + // -> + // ``` + // %1 = tuple_extract %t, 0 + // %2 = tuple_extract %t, 1 + // ``` + replaceWithAggregateExtract(context) + return + } } private func replaceWithAggregateExtract(_ context: SimplifyContext) { diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift index 93c93ca6d94b2..29ac9fa3f5913 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift @@ -26,6 +26,9 @@ extension LoadInst : OnoneSimplifiable, SILCombineSimplifiable { if replaceLoadOfGlobalLet(context) { return } + if tryRemoveAddressCast(context) { + return + } removeIfDead(context) } @@ -116,6 +119,27 @@ extension LoadInst : OnoneSimplifiable, SILCombineSimplifiable { return true } + /// Replaces address casts of heap objects + /// ``` + /// %1 = unchecked_addr_cast %0 : $*SomeClass to $*OtherClass + /// %2 = load [copy] %1 + /// ``` + /// with ref-casts of the loaded value + /// ``` + /// %1 = load [copy] %0 + /// %2 = unchecked_ref_cast %1 : $SomeClass to $OtherClass + /// ``` + private func tryRemoveAddressCast(_ context: SimplifyContext) -> Bool { + guard let addrCast = address.isAddressCastOfHeapObjects else { + return false + } + let builder = Builder(before: self, context) + let newLoad = builder.createLoad(fromAddress: addrCast.fromAddress, ownership: loadOwnership) + let cast = builder.createUncheckedRefCast(from: newLoad, to: addrCast.type.objectType) + replace(with: cast, context) + return true + } + private func isZeroLoadFromEmptyCollection() -> Bool { if !type.isBuiltinInteger { return false @@ -364,6 +388,18 @@ private extension Value { } } +extension Value { + var isAddressCastOfHeapObjects: UncheckedAddrCastInst? { + if let addrCast = self as? UncheckedAddrCastInst, + addrCast.fromAddress.type.isHeapObjectReferenceType, + addrCast.type.isHeapObjectReferenceType + { + return addrCast + } + return nil + } +} + private extension Instruction { var isShiftRightByAtLeastOne: Bool { guard let bi = self as? BuiltinInst, diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoadBorrow.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoadBorrow.swift new file mode 100644 index 0000000000000..0462ed6a92660 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoadBorrow.swift @@ -0,0 +1,116 @@ +//===--- SimplifyLoadBorrow.swift -----------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SIL + +extension LoadBorrowInst : Simplifiable, SILCombineSimplifiable { + func simplify(_ context: SimplifyContext) { + if uses.ignoreDebugUses.ignore(usersOfType: EndBorrowInst.self).isEmpty { + context.erase(instructionIncludingAllUsers: self) + return + } + + if tryCombineWithCopy(context) { + return + } + + if tryRemoveAddrCast(context) { + return + } + + tryForwardStoreBorrow(context) + } + + /// If the load_borrow is followed by a copy_value, combine both into a `load [copy]`: + /// ``` + /// %1 = load_borrow %0 + /// %2 = some_forwarding_instruction %1 // zero or more forwarding instructions + /// %3 = copy_value %2 + /// end_borrow %1 + /// ``` + /// -> + /// ``` + /// %1 = load [copy] %0 + /// %3 = some_forwarding_instruction %1 // zero or more forwarding instructions + /// ``` + /// + private func tryCombineWithCopy(_ context: SimplifyContext) -> Bool { + let forwardedValue = lookThroughOwnedConvertibaleForwardingChain() + guard let singleUser = forwardedValue.uses.ignore(usersOfType: EndBorrowInst.self).singleUse?.instruction, + let copy = singleUser as? CopyValueInst, + copy.parentBlock == self.parentBlock else { + return false + } + let builder = Builder(before: self, context) + let loadCopy = builder.createLoad(fromAddress: address, ownership: .copy) + let forwardedOwnedValue = replaceGuaranteed(value: self, withOwnedValue: loadCopy, context) + copy.replace(with: forwardedOwnedValue, context) + context.erase(instructionIncludingAllUsers: self) + return true + } + + /// Replaces address casts of heap objects + /// ``` + /// %1 = unchecked_addr_cast %0 : $*SomeClass to $*OtherClass + /// %2 = load_borrow %1 + /// // ... uses of %2 + /// end_borrow %2 + /// ``` + /// with ref-casts of the loaded value + /// ``` + /// %1 = load_borrow %0 + /// %2 = unchecked_ref_cast %1 : $SomeClass to $OtherClass + /// // ... uses of %2 + /// end_borrow %2 + /// ``` + /// Address casts are bad because they prevent alias analysis and AccessPath computation. + /// It's always better to use the corresponding value casts instead. + /// + private func tryRemoveAddrCast(_ context: SimplifyContext) -> Bool { + guard let addrCast = address.isAddressCastOfHeapObjects else { + return false + } + let builder = Builder(before: self, context) + let newLoad = builder.createLoadBorrow(fromAddress: addrCast.fromAddress) + let cast = builder.createUncheckedRefCast(from: newLoad, to: addrCast.type.objectType) + replace(with: newLoad, context) + newLoad.uses.filter{ !$0.endsLifetime }.ignore(user: cast).replaceAll(with: cast, context) + return true + } + + /// Replaces a `load_borrow` of a `store_borrow` with a `begin_borrow`: + /// ``` + /// %1 = alloc_stack $T + /// %2 = store_borrow %0 to %1 + /// ... + /// %3 = load_borrow %2 + /// // ... uses of %3 + /// end_borrow %3 + /// ``` + /// -> + /// ``` + /// %1 = alloc_stack $T + /// %2 = store_borrow %0 to %1 + /// ... + /// %3 = begin_borrow %0 + /// // ... uses of %3 + /// end_borrow %3 + /// ``` + private func tryForwardStoreBorrow(_ context: SimplifyContext) { + guard let storeBorrow = address as? StoreBorrowInst else { + return + } + let builder = Builder(before: self, context) + let beginBorrow = builder.createBeginBorrow(of: storeBorrow.source) + replace(with: beginBorrow, context) + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyMisc.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyTypeValue.swift similarity index 94% rename from SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyMisc.swift rename to SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyTypeValue.swift index 2f571a61768c6..332f41a455513 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyMisc.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyTypeValue.swift @@ -1,4 +1,4 @@ -//===--- SimplifyMisc.swift -----------------------------------------------===// +//===--- SimplifyTypeValue.swift ------------------------------------------===// // // This source file is part of the Swift.org open source project // diff --git a/SwiftCompilerSources/Sources/SIL/Type.swift b/SwiftCompilerSources/Sources/SIL/Type.swift index 26bd2b9c144ab..3e779cf1a1d63 100644 --- a/SwiftCompilerSources/Sources/SIL/Type.swift +++ b/SwiftCompilerSources/Sources/SIL/Type.swift @@ -92,6 +92,9 @@ public struct Type : TypeProperties, CustomStringConvertible, NoReflectionChildr bridged.isExactSuperclassOf(type.bridged) } + /// True if this type references a "ref" type that has a single pointer representation. + public var isHeapObjectReferenceType: Bool { bridged.isHeapObjectReferenceType() } + public func loweredInstanceTypeOfMetatype(in function: Function) -> Type { return canonicalType.instanceTypeOfMetatype.loweredType(in: function) } diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index f87ed97d38285..3eb6c0477a66c 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -290,6 +290,7 @@ struct BridgedType { BRIDGED_INLINE bool isEscapable(BridgedFunction f) const; BRIDGED_INLINE bool isExactSuperclassOf(BridgedType t) const; BRIDGED_INLINE bool isMarkedAsImmortal() const; + BRIDGED_INLINE bool isHeapObjectReferenceType() const; BRIDGED_INLINE bool isAddressableForDeps(BridgedFunction f) const; BRIDGED_INLINE SWIFT_IMPORT_UNSAFE BridgedASTType getRawLayoutSubstitutedLikeType() const; BRIDGED_INLINE SWIFT_IMPORT_UNSAFE BridgedASTType getRawLayoutSubstitutedCountType() const; diff --git a/include/swift/SIL/SILBridgingImpl.h b/include/swift/SIL/SILBridgingImpl.h index 36b256bdd1ddf..7a56d8dc1c734 100644 --- a/include/swift/SIL/SILBridgingImpl.h +++ b/include/swift/SIL/SILBridgingImpl.h @@ -398,6 +398,10 @@ bool BridgedType::isMarkedAsImmortal() const { return unbridged().isMarkedAsImmortal(); } +bool BridgedType::isHeapObjectReferenceType() const { + return unbridged().isHeapObjectReferenceType(); +} + bool BridgedType::isAddressableForDeps(BridgedFunction f) const { return unbridged().isAddressableForDeps(*f.getFunction()); } diff --git a/lib/IRGen/LoadableByAddress.cpp b/lib/IRGen/LoadableByAddress.cpp index 829912702b1ae..b4e2b1e74578b 100644 --- a/lib/IRGen/LoadableByAddress.cpp +++ b/lib/IRGen/LoadableByAddress.cpp @@ -4332,6 +4332,17 @@ class RewriteUser : SILInstructionVisitor { assignment.markForDeletion(bc); } + void visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *bc) { + auto addr = assignment.getAddressForValue(bc->getOperand()); + auto builder = assignment.getBuilder(bc->getIterator()); + auto loaded = builder.createLoad(bc->getLoc(), addr, + LoadOwnershipQualifier::Unqualified); + auto newVal = builder.createUncheckedBitwiseCast(bc->getLoc(), loaded, + bc->getType()); + bc->replaceAllUsesWith(newVal); + assignment.markForDeletion(bc); + } + void visitEnumInst(EnumInst *e) { assert(!assignment.isLargeLoadableType(e->getType())); auto opd = e->getOperand(); diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index b97d3de1f9a59..8c8bb09709040 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -1054,11 +1054,11 @@ BUILTIN_OPERAND_OWNERSHIP(BitwiseEscape, BuildMainActorExecutorRef) BUILTIN_OPERAND_OWNERSHIP(TrivialUse, AutoDiffCreateLinearMapContextWithType) // InstantaneousUse since we take in a closure at +0. -BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TaskAddCancellationHandler) +BUILTIN_OPERAND_OWNERSHIP(BitwiseEscape, TaskAddCancellationHandler) // Trivial use since our operand is just an UnsafeRawPointer. BUILTIN_OPERAND_OWNERSHIP(TrivialUse, TaskRemoveCancellationHandler) // InstantaneousUse since we take in a closure at +0. -BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TaskAddPriorityEscalationHandler) +BUILTIN_OPERAND_OWNERSHIP(BitwiseEscape, TaskAddPriorityEscalationHandler) // Trivial use since our operand is just an UnsafeRawPointer. BUILTIN_OPERAND_OWNERSHIP(TrivialUse, TaskRemovePriorityEscalationHandler) // This is a trivial use since our first operand is a Builtin.RawPointer and our diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index f17b2675690aa..3f70a3752218c 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -70,16 +70,6 @@ ParseIncompleteOSSA("parse-incomplete-ossa", //===----------------------------------------------------------------------===// SILParserState::~SILParserState() { - if (!ForwardRefFns.empty()) { - for (auto Entry : ForwardRefFns) { - if (Entry.second.Loc.isValid()) { - M.getASTContext().Diags.diagnose(Entry.second.Loc, - diag::sil_use_of_undefined_value, - Entry.first.str()); - } - } - } - // Turn any debug-info-only function declarations into zombies. markZombies(); } @@ -93,6 +83,21 @@ void SILParserState::markZombies() { } } +bool SILParserState::diagnoseUndefinedValues(DiagnosticEngine &diags) { + bool hasError = false; + if (!ForwardRefFns.empty()) { + for (auto Entry : ForwardRefFns) { + if (Entry.second.Loc.isValid()) { + diags.diagnose(Entry.second.Loc, + diag::sil_use_of_undefined_value, + Entry.first.str()); + hasError = true; + } + } + } + return hasError; +} + std::unique_ptr ParseSILModuleRequest::evaluate(Evaluator &evaluator, ASTLoweringDescriptor desc) const { @@ -111,6 +116,10 @@ ParseSILModuleRequest::evaluate(Evaluator &evaluator, } auto hadError = parser.parseTopLevelSIL(); + + if (parserState.diagnoseUndefinedValues(parser.Diags)) + hadError = true; + if (hadError) { // The rest of the SIL pipeline expects well-formed SIL, so if we encounter // a parsing error, just return an empty SIL module. diff --git a/lib/SIL/Parser/SILParserState.h b/lib/SIL/Parser/SILParserState.h index c18d2865cf112..7cb684ac1ee15 100644 --- a/lib/SIL/Parser/SILParserState.h +++ b/lib/SIL/Parser/SILParserState.h @@ -51,6 +51,8 @@ class SILParserState : public SILParserStateBase { /// Did we parse a sil_stage for this module? bool DidParseSILStage = false; + bool diagnoseUndefinedValues(DiagnosticEngine &diags); + bool parseDeclSIL(Parser &P) override; bool parseDeclSILStage(Parser &P) override; bool parseSILVTable(Parser &P) override; diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 534d0c3279648..040e59f2c532b 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -7311,95 +7311,96 @@ class SILVerifier : public SILVerifierBase { void visitSILFunction(SILFunction *F) { PrettyStackTraceSILFunction stackTrace("verifying", F); - std::optional functionTypeErrorGuard{{this, F}}; - CanSILFunctionType FTy = F->getLoweredFunctionType(); - verifySILFunctionType(FTy); - verifyParentFunctionSILFunctionType(FTy); - SILModule &mod = F->getModule(); - bool embedded = mod.getASTContext().LangOpts.hasFeature(Feature::Embedded); - - require(!F->isAnySerialized() || !mod.isSerialized() || mod.isParsedAsSerializedSIL(), - "cannot have a serialized function after the module has been serialized"); + { + VerifierErrorEmitterGuard functionTypeErrorGuard(this, F); + CanSILFunctionType FTy = F->getLoweredFunctionType(); + verifySILFunctionType(FTy); + verifyParentFunctionSILFunctionType(FTy); + + bool embedded = mod.getASTContext().LangOpts.hasFeature(Feature::Embedded); + + require(!F->isAnySerialized() || !mod.isSerialized() || mod.isParsedAsSerializedSIL(), + "cannot have a serialized function after the module has been serialized"); + + switch (F->getLinkage()) { + case SILLinkage::Public: + case SILLinkage::Package: + case SILLinkage::Shared: + require(F->isDefinition() || F->hasForeignBody(), + "public/package/shared function must have a body"); + break; + case SILLinkage::PublicNonABI: + case SILLinkage::PackageNonABI: + require(F->isDefinition(), + "alwaysEmitIntoClient function must have a body"); + require(F->isAnySerialized() || mod.isSerialized(), + "alwaysEmitIntoClient function must be serialized"); + break; + case SILLinkage::Hidden: + case SILLinkage::Private: + require(F->isDefinition() || F->hasForeignBody(), + "internal/private function must have a body"); + require(!F->isAnySerialized() || embedded, + "internal/private function cannot be serialized or serializable"); + break; + case SILLinkage::PublicExternal: + require(F->isExternalDeclaration() || + F->isAnySerialized() || + mod.isSerialized(), + "public-external function definition must be serialized"); + break; + case SILLinkage::PackageExternal: + require(F->isExternalDeclaration() || + F->isAnySerialized() || + mod.isSerialized(), + "package-external function definition must be serialized"); + break; + case SILLinkage::HiddenExternal: + require(F->isExternalDeclaration() || embedded, + "hidden-external function cannot have a body"); + break; + } - switch (F->getLinkage()) { - case SILLinkage::Public: - case SILLinkage::Package: - case SILLinkage::Shared: - require(F->isDefinition() || F->hasForeignBody(), - "public/package/shared function must have a body"); - break; - case SILLinkage::PublicNonABI: - case SILLinkage::PackageNonABI: - require(F->isDefinition(), - "alwaysEmitIntoClient function must have a body"); - require(F->isAnySerialized() || mod.isSerialized(), - "alwaysEmitIntoClient function must be serialized"); - break; - case SILLinkage::Hidden: - case SILLinkage::Private: - require(F->isDefinition() || F->hasForeignBody(), - "internal/private function must have a body"); - require(!F->isAnySerialized() || embedded, - "internal/private function cannot be serialized or serializable"); - break; - case SILLinkage::PublicExternal: - require(F->isExternalDeclaration() || - F->isAnySerialized() || - mod.isSerialized(), - "public-external function definition must be serialized"); - break; - case SILLinkage::PackageExternal: - require(F->isExternalDeclaration() || - F->isAnySerialized() || - mod.isSerialized(), - "package-external function definition must be serialized"); - break; - case SILLinkage::HiddenExternal: - require(F->isExternalDeclaration() || embedded, - "hidden-external function cannot have a body"); - break; - } + // Don't verify functions that were skipped. We are likely to see them in + // FunctionBodySkipping::NonInlinableWithoutTypes mode. + auto Ctx = F->getDeclContext(); + if (Ctx) { + if (auto AFD = dyn_cast(Ctx)) { + if (AFD->isBodySkipped()) + return; + } + } - // Don't verify functions that were skipped. We are likely to see them in - // FunctionBodySkipping::NonInlinableWithoutTypes mode. - auto Ctx = F->getDeclContext(); - if (Ctx) { - if (auto AFD = dyn_cast(Ctx)) { - if (AFD->isBodySkipped()) + if (F->isExternalDeclaration()) { + if (F->hasForeignBody()) return; - } - } - if (F->isExternalDeclaration()) { - if (F->hasForeignBody()) + require(F->isAvailableExternally(), + "external declaration of internal SILFunction not allowed"); + // If F is an external declaration, there is nothing further to do, + // return. return; + } - require(F->isAvailableExternally(), - "external declaration of internal SILFunction not allowed"); - // If F is an external declaration, there is nothing further to do, - // return. - return; - } - - require(!FTy->hasErasedIsolation(), - "function declarations cannot have erased isolation"); + require(!FTy->hasErasedIsolation(), + "function declarations cannot have erased isolation"); - assert(!F->hasForeignBody()); + assert(!F->hasForeignBody()); - // Make sure that our SILFunction only has context generic params if our - // SILFunctionType is non-polymorphic. - if (F->getGenericEnvironment() && - !F->getGenericEnvironment()->getGenericSignature() - ->areAllParamsConcrete()) { - require(FTy->isPolymorphic(), - "non-generic function definitions cannot have a " - "generic environment"); - } else { - require(!FTy->isPolymorphic(), - "generic function definition must have a generic environment"); + // Make sure that our SILFunction only has context generic params if our + // SILFunctionType is non-polymorphic. + if (F->getGenericEnvironment() && + !F->getGenericEnvironment()->getGenericSignature() + ->areAllParamsConcrete()) { + require(FTy->isPolymorphic(), + "non-generic function definitions cannot have a " + "generic environment"); + } else { + require(!FTy->isPolymorphic(), + "generic function definition must have a generic environment"); + } } - functionTypeErrorGuard.reset(); // Before verifying the body of the function, validate the SILUndef map to // make sure that all SILUndef in the function's map point at the function diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index e71e9b774d3cc..003ec43428140 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -214,9 +214,12 @@ static bool isInPrintFunctionList(SILFunction *F) { } bool isFunctionSelectedForPrinting(SILFunction *F) { - if (!SILPrintFunction.empty() && !isInPrintFunctionList(F)) + if (F->getName() != "$ss10_NativeSetV9insertNew_2at8isUniqueyxn_s10_HashTableV6BucketVSbtF13FoundationXML7XMLNodeC_Tg5") return false; +// if (!SILPrintFunction.empty() && !isInPrintFunctionList(F)) +// return false; + if (!F->getName().contains(SILPrintFunctions)) return false; @@ -255,7 +258,7 @@ void printInliningDetailsCallerAfter(StringRef passName, SILFunction *caller, } static bool functionSelectionEmpty() { - return SILPrintFunction.empty() && SILPrintFunctions.empty(); + return false; //SILPrintFunction.empty() && SILPrintFunctions.empty(); } bool SILPassManager::doPrintBefore(SILTransform *T, SILFunction *F) { @@ -928,8 +931,8 @@ void SILPassManager::runModulePass(unsigned TransIdx) { // If this pass invalidated anything, print and verify. if (doPrintAfter(SMT, nullptr, CurrentPassHasInvalidated)) { - dumpPassInfo("*** SIL module after", TransIdx); - printModule(Mod, Options.EmitVerboseSIL); +// dumpPassInfo("*** SIL module after", TransIdx); +// printModule(Mod, Options.EmitVerboseSIL); } updateSILModuleStatsAfterTransform(*Mod, SMT, *this, NumPassesRun, duration.count()); diff --git a/lib/SILOptimizer/SemanticARC/BorrowScopeOpts.cpp b/lib/SILOptimizer/SemanticARC/BorrowScopeOpts.cpp deleted file mode 100644 index b70f8bc9a8452..0000000000000 --- a/lib/SILOptimizer/SemanticARC/BorrowScopeOpts.cpp +++ /dev/null @@ -1,68 +0,0 @@ -//===--- BorrowScopeOpts.cpp ----------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// -/// Optimizations that attempt to simplify and or eliminate borrow scopes. Today -/// we only eliminate scopes, but we could also eliminate redundant scopes by -/// converting struct_extract operations to use destructure operations. -/// -//===----------------------------------------------------------------------===// - -#include "Context.h" -#include "SemanticARCOptVisitor.h" - -using namespace swift; -using namespace swift::semanticarc; - -bool SemanticARCOptVisitor::visitBeginBorrowInst(BeginBorrowInst *bbi) { - // Quickly check if we are supposed to perform this transformation. - if (!ctx.shouldPerform(ARCTransformKind::RedundantBorrowScopeElimPeephole)) - return false; - - // Non-redundant, lexical borrow scopes must remain in order to ensure that - // value lifetimes are not observably shortened. - if (bbi->isLexical() && !isNestedLexicalBeginBorrow(bbi)) { - return false; - } - - auto kind = bbi->getOperand()->getOwnershipKind(); - SmallVector endBorrows; - for (auto *op : bbi->getUses()) { - if (!op->isLifetimeEnding()) { - // Make sure that this operand can accept our arguments kind. - if (op->canAcceptKind(kind)) - continue; - return false; - } - - // Otherwise, this borrow is being consumed. See if our consuming inst is an - // end_borrow. If it isn't, then return false, this scope is - // needed. Otherwise, add the end_borrow to our list of end borrows. - auto *ebi = dyn_cast(op->getUser()); - if (!ebi) { - return false; - } - endBorrows.push_back(ebi); - } - - // At this point, we know that the begin_borrow's operand can be - // used as an argument to all non-end borrow uses. Eliminate the - // begin borrow and end borrows. - while (!endBorrows.empty()) { - auto *ebi = endBorrows.pop_back_val(); - eraseInstruction(ebi); - } - - eraseAndRAUWSingleValueInstruction(bbi, bbi->getOperand()); - return true; -} diff --git a/lib/SILOptimizer/SemanticARC/CMakeLists.txt b/lib/SILOptimizer/SemanticARC/CMakeLists.txt index 358162307cf39..62d60aefc5732 100644 --- a/lib/SILOptimizer/SemanticARC/CMakeLists.txt +++ b/lib/SILOptimizer/SemanticARC/CMakeLists.txt @@ -1,7 +1,6 @@ target_sources(swiftSILOptimizer PRIVATE SemanticARCOpts.cpp OwnershipLiveRange.cpp - BorrowScopeOpts.cpp CopyValueOpts.cpp OwnedToGuaranteedPhiOpt.cpp Context.cpp diff --git a/lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h b/lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h index 86487b26401ca..f8e4a4f10d441 100644 --- a/lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h +++ b/lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h @@ -142,7 +142,6 @@ struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor } bool visitCopyValueInst(CopyValueInst *cvi); - bool visitBeginBorrowInst(BeginBorrowInst *bbi); bool visitMoveValueInst(MoveValueInst *mvi); bool visitUncheckedOwnershipConversionInst(UncheckedOwnershipConversionInst *uoci); diff --git a/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp index 0bc29ade245a3..3f138cdd961d4 100644 --- a/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp @@ -36,9 +36,6 @@ static llvm::cl::list TransformsToPerform( clEnumValN(ARCTransformKind::LoadCopyToLoadBorrowPeephole, "sil-semantic-arc-peepholes-loadcopy-to-loadborrow", "Perform the load [copy] to load_borrow peephole"), - clEnumValN(ARCTransformKind::RedundantBorrowScopeElimPeephole, - "sil-semantic-arc-peepholes-redundant-borrowscope-elim", - "Perform the redundant borrow scope elimination peephole"), clEnumValN(ARCTransformKind::RedundantCopyValueElimPeephole, "sil-semantic-arc-peepholes-redundant-copyvalue-elim", "Perform the redundant copy_value peephole"), @@ -87,7 +84,6 @@ struct SemanticARCOpts : SILFunctionTransform { switch (transform) { case ARCTransformKind::LifetimeJoiningPeephole: case ARCTransformKind::RedundantCopyValueElimPeephole: - case ARCTransformKind::RedundantBorrowScopeElimPeephole: case ARCTransformKind::LoadCopyToLoadBorrowPeephole: case ARCTransformKind::AllPeepholes: case ARCTransformKind::OwnershipConversionElimPeephole: diff --git a/lib/SILOptimizer/SemanticARC/SemanticARCOpts.h b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.h index 0b51467f231ef..ea2ed4802a478 100644 --- a/lib/SILOptimizer/SemanticARC/SemanticARCOpts.h +++ b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.h @@ -25,7 +25,6 @@ enum class ARCTransformKind : uint64_t { Invalid = 0, OwnedToGuaranteedPhi = 0x1, LoadCopyToLoadBorrowPeephole = 0x2, - RedundantBorrowScopeElimPeephole = 0x4, // TODO: Split RedundantCopyValueElimPeephole into more granular categories // such as dead live range, guaranteed copy_value opt, etc. RedundantCopyValueElimPeephole = 0x8, @@ -34,7 +33,6 @@ enum class ARCTransformKind : uint64_t { RedundantMoveValueElim = 0x40, AllPeepholes = LoadCopyToLoadBorrowPeephole | - RedundantBorrowScopeElimPeephole | RedundantCopyValueElimPeephole | LifetimeJoiningPeephole | OwnershipConversionElimPeephole, All = AllPeepholes | OwnedToGuaranteedPhi | RedundantMoveValueElim, diff --git a/lib/SILOptimizer/Utils/OwnershipOptUtils.cpp b/lib/SILOptimizer/Utils/OwnershipOptUtils.cpp index abd02ac498bf6..41dfac49fefd0 100644 --- a/lib/SILOptimizer/Utils/OwnershipOptUtils.cpp +++ b/lib/SILOptimizer/Utils/OwnershipOptUtils.cpp @@ -886,7 +886,7 @@ OwnershipLifetimeExtender::createPlusZeroCopy(SILValue value, boundary.visitInsertionPoints( [&](SILBasicBlock::iterator insertPt) { SILBuilderWithScope builder(insertPt); - auto *dvi = builder.createDestroyValue(insertPt->getLoc(), copy); + auto *dvi = builder.createDestroyValue(RegularLocation(insertPt->getLoc()), copy); callbacks.createdNewInst(dvi); }, &ctx.deBlocks); diff --git a/test/ClangImporter/serialization-sil.swift b/test/ClangImporter/serialization-sil.swift index af2e624046a97..91b94bd365b87 100644 --- a/test/ClangImporter/serialization-sil.swift +++ b/test/ClangImporter/serialization-sil.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -enable-copy-propagation=requested-passes-only -enable-lexical-lifetimes=false -Xllvm -sil-disable-pass=Simplification -emit-module-path %t/Test.swiftmodule -Xllvm -sil-print-types -emit-sil -o /dev/null -module-name Test %s -sdk "" -import-objc-header %S/Inputs/serialization-sil.h +// RUN: %target-swift-frontend -enable-copy-propagation=requested-passes-only -enable-lexical-lifetimes=false -Xllvm -simplify-instruction=begin_borrow -emit-module-path %t/Test.swiftmodule -Xllvm -sil-print-types -emit-sil -o /dev/null -module-name Test %s -sdk "" -import-objc-header %S/Inputs/serialization-sil.h // RUN: %target-sil-func-extractor -sil-print-types %t/Test.swiftmodule -sil-print-debuginfo -func='$s4Test16testPartialApplyyySoAA_pF' -o - | %FileCheck %s // REQUIRES: objc_interop diff --git a/test/SIL/verifier-undefined-function.sil b/test/SIL/verifier-undefined-function.sil new file mode 100644 index 0000000000000..d90a8b5c24fc0 --- /dev/null +++ b/test/SIL/verifier-undefined-function.sil @@ -0,0 +1,16 @@ +// RUN: not %target-sil-opt -o /dev/null %s 2>&1 | %FileCheck %s + +// REQUIRES: asserts + +sil_stage canonical + +import Builtin + +sil @undefined_function_ref : $@convention(thin) () -> () { +bb0: + // CHECK: :[[@LINE+1]]:21: error: use of undefined value 'undefined_function' + %0 = function_ref @undefined_function : $@convention(thin) () -> () + %r = tuple () + return %r +} + diff --git a/test/SIL/verifier_failures.sil b/test/SIL/verifier_failures.sil index f2f3160349b99..df2a3db04b36c 100644 --- a/test/SIL/verifier_failures.sil +++ b/test/SIL/verifier_failures.sil @@ -138,3 +138,12 @@ bb2: %7 = end_apply %4 as $() unwind } + +// CHECK-LABEL: Begin Error in function private_without_body +// CHECK: SIL verification failed: internal/private function must have a body +// CHECK: End Error in function private_without_body +// CHECK-LABEL: Begin Error in function private_without_body +// CHECK: SIL verification failed: external declaration of internal SILFunction not allowed +// CHECK: End Error in function private_without_body +sil private @private_without_body : $@convention(thin) () -> () + diff --git a/test/SILOptimizer/copy-to-borrow-optimization.sil b/test/SILOptimizer/copy-to-borrow-optimization.sil index 3f27a8cf4f2a4..7915d4a20e12a 100644 --- a/test/SILOptimizer/copy-to-borrow-optimization.sil +++ b/test/SILOptimizer/copy-to-borrow-optimization.sil @@ -1,5 +1,4 @@ // RUN: %target-sil-opt -copy-to-borrow-optimization %s | %FileCheck %s -// REQUIRES: macosx sil_stage canonical @@ -82,6 +81,22 @@ struct NonTrivialStruct { var val: Klass } +class MyArrayStorageBase { + @_hasStorage let countAndCapacity: Int +} + +struct MyBridgeStorage { + let rawValue: Builtin.NativeObject +} + +struct MyArrayBuffer { + let storage: MyBridgeStorage +} + +struct MyArray { + let buffer: MyArrayBuffer +} + sil @getKlass : $@convention(thin) () -> @owned Klass sil @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> () sil @use_inguaranteed : $@convention(thin) (@in_guaranteed Klass) -> () @@ -2242,21 +2257,40 @@ bb1(%1 : @guaranteed $C): // CHECK-LABEL: sil [ossa] @borrowed_from_forward2 : {{.*}} { // CHECK-NOT: copy_value // CHECK-LABEL: } // end sil function 'borrowed_from_forward2' -sil [ossa] @borrowed_from_forward2 : $@convention(thin) (@guaranteed Array) -> () { -bb0(%0 : @guaranteed $Array): - %1 = struct_extract %0, #Array._buffer - %2 = struct_extract %1, #_ArrayBuffer._storage - %3 = struct_extract %2, #_BridgeStorage.rawValue - %4 = unchecked_ref_cast %3 to $__ContiguousArrayStorageBase +sil [ossa] @borrowed_from_forward2 : $@convention(thin) (@guaranteed MyArray) -> () { +bb0(%0 : @guaranteed $MyArray): + %1 = struct_extract %0, #MyArray.buffer + %2 = struct_extract %1, #MyArrayBuffer.storage + %3 = struct_extract %2, #MyBridgeStorage.rawValue + %4 = unchecked_ref_cast %3 to $MyArrayStorageBase br bb1(%4) -bb1(%6 : @guaranteed $__ContiguousArrayStorageBase): +bb1(%6 : @guaranteed $MyArrayStorageBase): %7 = borrowed %6 from (%0) %8 = copy_value %7 %9 = begin_borrow %8 - %10 = ref_element_addr [immutable] %9, #__ContiguousArrayStorageBase.countAndCapacity + %10 = ref_element_addr [immutable] %9, #MyArrayStorageBase.countAndCapacity end_borrow %9 destroy_value %8 %13 = tuple () return %13 } + +// CHECK-LABEL: sil [ossa] @test_updating_borrowed_from : +// CHECK: borrowed {{.*}} from (%0) +// CHECK-LABEL: } // end sil function 'test_updating_borrowed_from' +sil [ossa] @test_updating_borrowed_from : $@convention(thin) (@guaranteed NonTrivialStruct) -> () { +bb0(%0 : @guaranteed $NonTrivialStruct): + %1 = destructure_struct %0 : $NonTrivialStruct + %2 = copy_value %1 : $Klass + %3 = begin_borrow %2 : $Klass + br bb1(%3 : $Klass) + +bb1(%5 : @reborrow $Klass): + %6 = borrowed %5 : $Klass from (%2 : $Klass) + end_borrow %6 : $Klass + destroy_value %2 : $Klass + %9 = tuple () + return %9 : $() +} + diff --git a/test/SILOptimizer/copy_propagation_non_lexical.sil b/test/SILOptimizer/copy_propagation_non_lexical.sil index 65ece58020aaf..721f1fe190780 100644 --- a/test/SILOptimizer/copy_propagation_non_lexical.sil +++ b/test/SILOptimizer/copy_propagation_non_lexical.sil @@ -27,4 +27,34 @@ bb0(%0 : @owned $Builtin.BridgeObject): return %8 } +// CHECK-LABEL: sil [ossa] @test_add_cancellation_handler : +// CHECK: builtin "taskAddCancellationHandler" +// CHECK-NEXT: builtin "taskRemoveCancellationHandler" +// CHECK-NEXT: destroy_value +// CHECK-LABEL: } // end sil function 'test_add_cancellation_handler' +sil [ossa] @test_add_cancellation_handler : $@convention(thin) @async () -> () { +bb0: + %0 = partial_apply [on_stack] undef() : $@convention(thin) @Sendable () -> () + %1 = builtin "taskAddCancellationHandler"(%0) : $UnsafeRawPointer + %2 = builtin "taskRemoveCancellationHandler"(%1) : $() + destroy_value %0 + %4 = tuple () + return %4 +} + +// CHECK-LABEL: sil [ossa] @test_add_priority_escalation_handler : +// CHECK: builtin "taskAddPriorityEscalationHandler" +// CHECK-NEXT: builtin "taskRemovePriorityEscalationHandler" +// CHECK-NEXT: destroy_value +// CHECK-LABEL: } // end sil function 'test_add_priority_escalation_handler' +sil [ossa] @test_add_priority_escalation_handler : $@convention(thin) @async () -> () { +bb0: + %0 = partial_apply [on_stack] undef() : $@convention(thin) @Sendable (UInt8, UInt8) -> () + %1 = builtin "taskAddPriorityEscalationHandler"(%0) : $UnsafeRawPointer + %2 = builtin "taskRemovePriorityEscalationHandler"(%1) : $() + destroy_value %0 + %4 = tuple () + return %4 +} + diff --git a/test/SILOptimizer/pre_specialize_layouts.swift b/test/SILOptimizer/pre_specialize_layouts.swift index 97c91b41f3b6b..d30e0ecb871e7 100644 --- a/test/SILOptimizer/pre_specialize_layouts.swift +++ b/test/SILOptimizer/pre_specialize_layouts.swift @@ -158,7 +158,8 @@ internal func testEmitIntoClient(t: T) { // OPT: [[R8:%.*]] = unchecked_addr_cast [[R7]] : $*AnyObject to $*SomeClass // OPT: [[F6:%.*]] = function_ref @$s30pre_specialized_module_layouts14InternalThing2V9computedZxvryXl_Ts5 : $@yield_once @convention(method) (@guaranteed InternalThing2) -> @yields @in_guaranteed AnyObject // OPT: ([[R9:%.*]], {{%.*}}) = begin_apply [[F6]]({{.*}}) : $@yield_once @convention(method) (@guaranteed InternalThing2) -> @yields @in_guaranteed AnyObject -// OPT: [[R10:%.*]] = unchecked_addr_cast [[R9]] : $*AnyObject to $*SomeClass +// OPT: [[LD:%.*]] = load [[R9]] +// OPT: [[R10:%.*]] = unchecked_ref_cast [[LD]] : $AnyObject to $SomeClass // OPT: } // end sil function '$s30pre_specialized_module_layouts16useInternalThingyyxlFAA9SomeClassC_Tg5' public func usePrespecializedEntryPoints(wrapperStruct: ReferenceWrapperStruct, overaligned: OveralignedReferenceWrapperStruct, array: [Int], stride96: Stride96) { diff --git a/test/SILOptimizer/semantic-arc-opts-canonical.sil b/test/SILOptimizer/semantic-arc-opts-canonical.sil index f20e365b7fcaf..2a683f314e534 100644 --- a/test/SILOptimizer/semantic-arc-opts-canonical.sil +++ b/test/SILOptimizer/semantic-arc-opts-canonical.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -sil-print-types -module-name Swift -enable-sil-verify-all -semantic-arc-opts %s | %FileCheck %s +// RUN: %target-sil-opt -sil-print-types -module-name Swift -enable-sil-verify-all -semantic-arc-opts -onone-simplification -simplify-instruction=begin_borrow %s | %FileCheck %s // REQUIRES: swift_in_compiler diff --git a/test/SILOptimizer/semantic-arc-opts.sil b/test/SILOptimizer/semantic-arc-opts.sil index 866312f9ba754..feaab9281ac2b 100644 --- a/test/SILOptimizer/semantic-arc-opts.sil +++ b/test/SILOptimizer/semantic-arc-opts.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -sil-print-types -module-name Swift -enable-sil-verify-all -semantic-arc-opts %s | %FileCheck %s +// RUN: %target-sil-opt -sil-print-types -module-name Swift -enable-sil-verify-all -semantic-arc-opts -onone-simplification -simplify-instruction=begin_borrow %s | %FileCheck %s sil_stage raw diff --git a/test/SILOptimizer/sil_combine_misc_opts.sil b/test/SILOptimizer/sil_combine_misc_opts.sil index 0826e7fba3cd3..2b49f934e40d1 100644 --- a/test/SILOptimizer/sil_combine_misc_opts.sil +++ b/test/SILOptimizer/sil_combine_misc_opts.sil @@ -102,7 +102,7 @@ bb0(%0 : $*Builtin.NativeObject): } // CHECK-LABEL: sil [ossa] @test_updating_borrowed_from : -// CHECK: borrowed {{.*}} from (%0 : $S) +// this is not optimized anymore in SILCombine // CHECK-LABEL: } // end sil function 'test_updating_borrowed_from' sil [ossa] @test_updating_borrowed_from : $@convention(thin) (@guaranteed S) -> () { bb0(%0 : @guaranteed $S): diff --git a/test/SILOptimizer/simplify_begin_borrow.sil b/test/SILOptimizer/simplify_begin_borrow.sil index 429553ef194c8..63039fb73af53 100644 --- a/test/SILOptimizer/simplify_begin_borrow.sil +++ b/test/SILOptimizer/simplify_begin_borrow.sil @@ -1,4 +1,5 @@ -// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -onone-simplification -simplify-instruction=begin_borrow | %FileCheck %s +// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -onone-simplification -simplify-instruction=begin_borrow | %FileCheck %s --check-prefix=CHECK --check-prefix=ONONE +// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -simplification -simplify-instruction=begin_borrow | %FileCheck %s --check-prefix=CHECK --check-prefix=OPT // REQUIRES: swift_in_compiler @@ -7,6 +8,7 @@ sil_stage canonical import Builtin import Swift import SwiftShims +import _Concurrency class B {} class C : B { @@ -51,9 +53,10 @@ bb0(%0 : @owned $C): } // CHECK-LABEL: sil [ossa] @replace_with_single_forward : -// CHECK: %1 = struct $S1 (%0 : $C) -// CHECK: apply %2(%1) -// CHECK-NEXT: destroy_value %1 +// CHECK: debug_value %0 : $C, let, name "x" +// CHECK: %2 = struct $S1 (%0 : $C) +// CHECK: apply %3(%2) +// CHECK-NEXT: destroy_value %2 // CHECK: } // end sil function 'replace_with_single_forward' sil [ossa] @replace_with_single_forward : $@convention(thin) (@owned C) -> () { bb0(%0 : @owned $C): @@ -61,6 +64,7 @@ bb0(%0 : @owned $C): %2 = struct $S1(%1 : $C) %3 = function_ref @takeS1 : $@convention(thin) (@guaranteed S1) -> () %4 = apply %3(%2) : $@convention(thin) (@guaranteed S1) -> () + debug_value %1, let, name "x" end_borrow %1 : $C destroy_value %0: $C %6 = tuple () @@ -68,10 +72,13 @@ bb0(%0 : @owned $C): } // CHECK-LABEL: sil [ossa] @replace_with_double_forward : -// CHECK: %1 = struct $S1 (%0 : $C) -// CHECK-NEXT: %2 = destructure_struct %1 -// CHECK: apply %3(%2) -// CHECK-NEXT: destroy_value %2 +// CHECK: debug_value %0 : $C, let, name "x" +// CHECK: %2 = struct $S1 (%0 : $C) +// CHECK: debug_value %2 : $S1, let, name "y" +// CHECK: %4 = destructure_struct %2 +// CHECK: debug_value %4 : $C, let, name "z" +// CHECK: apply %6(%4) +// CHECK-NEXT: destroy_value %4 // CHECK: } // end sil function 'replace_with_double_forward' sil [ossa] @replace_with_double_forward : $@convention(thin) (@owned C) -> () { bb0(%0 : @owned $C): @@ -80,6 +87,9 @@ bb0(%0 : @owned $C): %3 = struct_extract %2 : $S1, #S1.c %4 = function_ref @takeC : $@convention(thin) (@guaranteed C) -> () %5 = apply %4(%3) : $@convention(thin) (@guaranteed C) -> () + debug_value %1, let, name "x" + debug_value %2, let, name "y" + debug_value %3, let, name "z" end_borrow %1 : $C destroy_value %0: $C %6 = tuple () @@ -135,10 +145,10 @@ bb0(%0 : @owned $S1): return %0 : $S1 } -// CHECK-LABEL: sil [ossa] @dont_replace_guaranteed_op : -// CHECK: begin_borrow -// CHECK: } // end sil function 'dont_replace_guaranteed_op' -sil [ossa] @dont_replace_guaranteed_op : $@convention(thin) (@guaranteed C) -> () { +// CHECK-LABEL: sil [ossa] @remove_inner_borrow_of_guaranteed_arg : +// CHECK-NOT: begin_borrow +// CHECK: } // end sil function 'remove_inner_borrow_of_guaranteed_arg' +sil [ossa] @remove_inner_borrow_of_guaranteed_arg : $@convention(thin) (@guaranteed C) -> () { bb0(%0 : @guaranteed $C): %1 = begin_borrow %0 : $C %2 = function_ref @takeC : $@convention(thin) (@guaranteed C) -> () @@ -149,7 +159,8 @@ bb0(%0 : @guaranteed $C): } // CHECK-LABEL: sil [ossa] @dont_replace_lexical : -// CHECK: begin_borrow +// ONONE: begin_borrow +// OPT-NOT: begin_borrow // CHECK: } // end sil function 'dont_replace_lexical' sil [ossa] @dont_replace_lexical : $@convention(thin) (@owned C) -> () { bb0(%0 : @owned $C): @@ -480,3 +491,51 @@ bb1(%7 : @reborrow $@callee_guaranteed () -> ()): return %r } +// CHECK-LABEL: sil [ossa] @dont_remove_lexical_borrow_of_thin_function : +// ONONE: begin_borrow +// OPT-NOT: begin_borrow +// CHECK-LABEL: } // end sil function 'dont_remove_lexical_borrow_of_thin_function' +sil [ossa] @dont_remove_lexical_borrow_of_thin_function : $@convention(thin) () -> () { +bb0: + %0 = function_ref @simple_func : $@convention(thin) () -> () + %1 = thin_to_thick_function %0 to $@callee_guaranteed () -> () + %2 = begin_borrow [lexical] %1 + %3 = function_ref @use_closure : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () + %4 = apply %3(%2) : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () + %5 = apply %2(): $@callee_guaranteed () -> () + end_borrow %2 + %r = tuple () + return %r +} + +// CHECK-LABEL: sil [ossa] @convert_to_owned_ignoring_type_dependent_operands : +// CHECK-NOT: begin_borrow +// CHECK-LABEL: } // end sil function 'convert_to_owned_ignoring_type_dependent_operands' +sil [ossa] @convert_to_owned_ignoring_type_dependent_operands : $@convention(thin) (@owned AnyObject) -> () { +bb0(%0 : @owned $AnyObject): + %1 = begin_borrow %0 + %2 = open_existential_ref %1 to $@opened("E5D03528-36AD-11E8-A0AB-D0817AD47398", AnyObject) Self + %3 = metatype $@thick (@opened("E5D03528-36AD-11E8-A0AB-D0817AD47398", AnyObject) Self).Type + fix_lifetime %3 + %4 = unchecked_ref_cast %2 to $B + %5 = apply undef(%4) : $@convention(thin) (@guaranteed B) -> () + end_borrow %1 + destroy_value %0 + %6 = tuple () + return %6 : $() +} + +// CHECK-LABEL: sil [ossa] @dont_convert_none_to_guaranteed : +// CHECK: begin_borrow +// CHECK-LABEL: } // end sil function 'dont_convert_none_to_guaranteed' +sil [ossa] @dont_convert_none_to_guaranteed : $@convention(thin) @async () -> () { +bb0: + %0 = enum $Optional, #Optional.none!enumelt + %1 = unchecked_value_cast %0 to $Builtin.ImplicitActor + %2 = begin_borrow [lexical] %1 + %3 = implicitactor_to_opaqueisolation_cast %2 + %4 = apply undef(%3) : $@convention(thin) @async (@sil_isolated @guaranteed Optional) -> () + end_borrow %2 + %6 = tuple () + return %6 +} diff --git a/test/SILOptimizer/simplify_destructure_struct.sil b/test/SILOptimizer/simplify_destructure_struct.sil index 188d68e8b4db6..5b49c28a148b5 100644 --- a/test/SILOptimizer/simplify_destructure_struct.sil +++ b/test/SILOptimizer/simplify_destructure_struct.sil @@ -158,3 +158,16 @@ bb0(%0 : $TrivialS): return %3 } +// CHECK-LABEL: sil [ossa] @guaranteed_to_struct_extract : +// CHECK: %1 = struct_extract %0 : $S, #S.a +// CHECK: %2 = struct_extract %0 : $S, #S.b +// CHECK: } // end sil function 'guaranteed_to_struct_extract' +sil [ossa] @guaranteed_to_struct_extract : $@convention(thin) (@guaranteed S) -> () { +bb0(%0 : @guaranteed $S): + (%1, %2) = destructure_struct %0 + fix_lifetime %1 + fix_lifetime %2 + %r = tuple () + return %r +} + diff --git a/test/SILOptimizer/simplify_destructure_tuple.sil b/test/SILOptimizer/simplify_destructure_tuple.sil index 3dfadcdbe8b75..1a76b1805ea33 100644 --- a/test/SILOptimizer/simplify_destructure_tuple.sil +++ b/test/SILOptimizer/simplify_destructure_tuple.sil @@ -153,3 +153,16 @@ bb0(%0 : $(Int, Int)): return %3 } +// CHECK-LABEL: sil [ossa] @guaranteed_to_tuple_extract : +// CHECK: %1 = tuple_extract %0 : $(String, Int), 0 +// CHECK: %2 = tuple_extract %0 : $(String, Int), 1 +// CHECK: } // end sil function 'guaranteed_to_tuple_extract' +sil [ossa] @guaranteed_to_tuple_extract : $@convention(thin) (@guaranteed (String, Int)) -> () { +bb0(%0 : @guaranteed $(String, Int)): + (%1, %2) = destructure_tuple %0 + fix_lifetime %1 + fix_lifetime %2 + %r = tuple () + return %r +} + diff --git a/test/SILOptimizer/simplify_load.sil b/test/SILOptimizer/simplify_load.sil index 93fe07a2d6a57..bb463fa5e8a66 100644 --- a/test/SILOptimizer/simplify_load.sil +++ b/test/SILOptimizer/simplify_load.sil @@ -31,6 +31,10 @@ class D : C { @_hasStorage var d: Builtin.Int64 } +struct AO { + let a: AnyObject +} + sil_global hidden [let] @gb : $B sil_global hidden [let] @gb2 : $(B, Int64) @@ -543,3 +547,45 @@ bb0: return %4 } +// CHECK-LABEL: sil [ossa] @load_of_addr_cast : +// CHECK: %1 = load [copy] %0 +// CHECK: %2 = unchecked_ref_cast %1 : $B to $AnyObject +// CHECK: fix_lifetime %2 +// CHECK: destroy_value %2 +// CHECK: } // end sil function 'load_of_addr_cast' +sil [ossa] @load_of_addr_cast : $@convention(thin) (@inout B) -> () { +bb0(%0 : $*B): + %1 = unchecked_addr_cast %0 to $*AnyObject + %2 = load [copy] %1 + fix_lifetime %2 + destroy_value %2 + %6 = tuple () + return %6 +} + +// CHECK-LABEL: sil [ossa] @dont_remove_non_class_addr_cast1 : +// CHECK: %1 = unchecked_addr_cast %0 +// CHECK: } // end sil function 'dont_remove_non_class_addr_cast1' +sil [ossa] @dont_remove_non_class_addr_cast1 : $@convention(thin) (@inout AO) -> () { +bb0(%0 : $*AO): + %1 = unchecked_addr_cast %0 to $*AnyObject + %2 = load [copy] %1 + fix_lifetime %2 + destroy_value %2 + %6 = tuple () + return %6 +} + +// CHECK-LABEL: sil [ossa] @dont_remove_non_class_addr_cast2 : +// CHECK: %1 = unchecked_addr_cast %0 +// CHECK: } // end sil function 'dont_remove_non_class_addr_cast2' +sil [ossa] @dont_remove_non_class_addr_cast2 : $@convention(thin) (@inout AnyObject) -> () { +bb0(%0 : $*AnyObject): + %1 = unchecked_addr_cast %0 to $*AO + %2 = load [copy] %1 + fix_lifetime %2 + destroy_value %2 + %6 = tuple () + return %6 +} + diff --git a/test/SILOptimizer/simplify_load_borrow.sil b/test/SILOptimizer/simplify_load_borrow.sil index abb80f3cc8445..a623274fbf01a 100644 --- a/test/SILOptimizer/simplify_load_borrow.sil +++ b/test/SILOptimizer/simplify_load_borrow.sil @@ -93,3 +93,48 @@ bb0(%0 : $*B): %4 = tuple () return %4 } + +// CHECK-LABEL: sil [ossa] @load_of_addr_cast : +// CHECK: %1 = load_borrow %0 +// CHECK: %2 = unchecked_ref_cast %1 to $AnyObject +// CHECK: fix_lifetime %2 +// CHECK: end_borrow %1 +// CHECK: } // end sil function 'load_of_addr_cast' +sil [ossa] @load_of_addr_cast : $@convention(thin) (@inout B) -> () { +bb0(%0 : $*B): + %1 = unchecked_addr_cast %0 to $*AnyObject + %2 = load_borrow %1 + fix_lifetime %2 + end_borrow %2 + %6 = tuple () + return %6 +} + +// CHECK-LABEL: sil [ossa] @load_of_store_borrow : +// CHECK: bb1: +// CHECK-NEXT: %4 = begin_borrow %0 +// CHECK-NEXT: fix_lifetime %4 +// CHECK-NEXT: end_borrow %4 +// CHECK: } // end sil function 'load_of_store_borrow' +sil [ossa] @load_of_store_borrow : $@convention(thin) (@guaranteed B) -> () { +bb0(%0 : @guaranteed $B): + %1 = alloc_stack $B + %2 = store_borrow %0 to %1 + cond_br undef, bb1, bb2 + +bb1: + %4 = load_borrow %2 + fix_lifetime %4 + end_borrow %4 + br bb3 + +bb2: + br bb3 + +bb3: + end_borrow %2 + dealloc_stack %1 + %r = tuple () + return %r +} +