From 610539a85f66ce5abce718d263ecb6c53a1c7bff Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 16 Oct 2025 10:12:33 +0200 Subject: [PATCH] SIL: streamline Operand Sequence APIs * remove `filterUsers(ofType:)`, because it's a duplication of `users(ofType:)` * rename `filterUses(ofType:)` -> `filter(usersOfType:)` * rename `ignoreUses(ofType:)` -> `ignore(usersOfType:)` * rename `getSingleUser` -> `singleUser` * implement `singleUse` with `Sequence.singleElement` * implement `ignoreDebugUses` with `ignore(usersOfType:)` This is a follow-up of https://github.com/swiftlang/swift/pull/83728/commits/eb1d5f484c9f4dae73a3779191bfdf917fd07a49. --- .../FunctionPasses/AllocBoxToStack.swift | 4 +-- .../ConstantCapturePropagation.swift | 2 +- .../FunctionPasses/DestroyHoisting.swift | 2 +- .../InitializeStaticGlobals.swift | 2 +- .../TempRValueElimination.swift | 4 +-- .../SimplifyAllocStack.swift | 4 +-- .../SimplifyBeginAndLoadBorrow.swift | 20 ++++++------- .../MandatoryPerformanceOptimizations.swift | 2 +- .../ModulePasses/StackProtection.swift | 2 +- .../Optimizer/Utilities/OptUtils.swift | 4 +-- .../Utilities/OwnershipLiveness.swift | 2 +- .../Sources/SIL/Operand.swift | 29 +++++-------------- .../Sources/SIL/Utilities/BorrowUtils.swift | 2 +- .../Sources/SIL/Utilities/PhiUpdater.swift | 2 +- 14 files changed, 34 insertions(+), 47 deletions(-) diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/AllocBoxToStack.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/AllocBoxToStack.swift index 6956c1078ec8d..18375d07bb32c 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/AllocBoxToStack.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/AllocBoxToStack.swift @@ -363,7 +363,7 @@ private func createAllocStack(for allocBox: AllocBoxInst, flags: Flags, _ contex isLexical: flags.isLexical, isFromVarDecl: flags.isFromVarDecl) let stackLocation: Value - if let mu = allocBox.uses.getSingleUser(ofType: MarkUninitializedInst.self) { + if let mu = allocBox.uses.singleUser(ofType: MarkUninitializedInst.self) { stackLocation = builder.createMarkUninitialized(value: asi, kind: mu.kind) } else { stackLocation = asi @@ -484,7 +484,7 @@ private func hoistMarkUnresolvedInsts(stackAddress: Value, builder = Builder(atBeginOf: stackAddress.parentBlock, context) } let mu = builder.createMarkUnresolvedNonCopyableValue(value: stackAddress, checkKind: checkKind, isStrict: false) - stackAddress.uses.ignore(user: mu).ignoreDebugUses.ignoreUses(ofType: DeallocStackInst.self) + stackAddress.uses.ignore(user: mu).ignoreDebugUses.ignore(usersOfType: DeallocStackInst.self) .replaceAll(with: mu, context) } diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ConstantCapturePropagation.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ConstantCapturePropagation.swift index 1f3abb6e6b1f1..cd00b047009e4 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ConstantCapturePropagation.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ConstantCapturePropagation.swift @@ -233,7 +233,7 @@ private func rewritePartialApply(_ partialApply: PartialApplyInst, withSpecializ // leave the key path itself out of the dependency chain, and introduce dependencies on those // operands instead, so that the key path object itself can be made dead. for md in newClosure.uses.users(ofType: MarkDependenceInst.self) { - if md.base.uses.getSingleUser(ofType: PartialApplyInst.self) == partialApply { + if md.base.uses.singleUser(ofType: PartialApplyInst.self) == partialApply { md.replace(with: newClosure, context) } } diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DestroyHoisting.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DestroyHoisting.swift index 42f521711cdbe..99e628a6d632d 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DestroyHoisting.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DestroyHoisting.swift @@ -83,7 +83,7 @@ let destroyHoisting = FunctionPass(name: "destroy-hoisting") { private func optimize(value: Value, _ context: FunctionPassContext) { guard value.ownership == .owned, // Avoid all the analysis effort if there are no destroys to hoist. - !value.uses.filterUses(ofType: DestroyValueInst.self).isEmpty + !value.uses.filter(usersOfType: DestroyValueInst.self).isEmpty else { return } diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift index 2526ac706327d..1d0b196edfdaa 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift @@ -478,7 +478,7 @@ private struct InitValueBuilder: AddressDefUseWalker { case .PrepareInitialization: return .continueWalk case .AddressOfRawLayout: - if let addr2Ptr = bi.uses.getSingleUser(ofType: PointerToAddressInst.self) { + if let addr2Ptr = bi.uses.singleUser(ofType: PointerToAddressInst.self) { return walkDownUses(ofAddress: addr2Ptr, path: path) } return .abortWalk diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/TempRValueElimination.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/TempRValueElimination.swift index dc908b7b6584e..491d048ba972d 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/TempRValueElimination.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/TempRValueElimination.swift @@ -83,7 +83,7 @@ private func tryEliminate(copy: CopyLikeInstruction, keepDebugInfo: Bool, _ cont removeDestroys(of: allocStack, context) } - allocStack.uses.ignoreUses(ofType: DeallocStackInst.self).replaceAll(with: copy.sourceAddress, context) + allocStack.uses.ignore(usersOfType: DeallocStackInst.self).replaceAll(with: copy.sourceAddress, context) if keepDebugInfo { Builder(before: copy, context).createDebugStep() @@ -247,7 +247,7 @@ private extension AllocStackInst { var liferange = InstructionRange(begin: self, context) defer { liferange.deinitialize() } - liferange.insert(contentsOf: uses.ignoreUses(ofType: DeallocStackInst.self).lazy.map { $0.instruction }) + liferange.insert(contentsOf: uses.ignore(usersOfType: DeallocStackInst.self).lazy.map { $0.instruction }) for use in uses { switch use.instruction { diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyAllocStack.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyAllocStack.swift index 4af01ed30b8a7..5c99b3d8f8d4d 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyAllocStack.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyAllocStack.swift @@ -207,7 +207,7 @@ private extension AllocStackInst { iea.replace(with: newAlloc, context) } case let oea as OpenExistentialAddrInst: - assert(oea.uses.ignoreUses(ofType: DestroyAddrInst.self).isEmpty) + assert(oea.uses.ignore(usersOfType: DestroyAddrInst.self).isEmpty) oea.replace(with: newAlloc, context) case let cab as CheckedCastAddrBranchInst: let builder = Builder(before: cab, context) @@ -247,7 +247,7 @@ private extension AllocStackInst { is DebugValueInst: break case let oea as OpenExistentialAddrInst: - if !oea.uses.ignoreUses(ofType: DestroyAddrInst.self).isEmpty { + if !oea.uses.ignore(usersOfType: DestroyAddrInst.self).isEmpty { return nil } case let iea as InitExistentialAddrInst: diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginAndLoadBorrow.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginAndLoadBorrow.swift index 0771dd69555d0..ddf5d8860760d 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginAndLoadBorrow.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginAndLoadBorrow.swift @@ -29,7 +29,7 @@ extension BeginBorrowInst : OnoneSimplifiable, SILCombineSimplifiable { extension LoadBorrowInst : Simplifiable, SILCombineSimplifiable { func simplify(_ context: SimplifyContext) { - if uses.ignoreDebugUses.ignoreUses(ofType: EndBorrowInst.self).isEmpty { + if uses.ignoreDebugUses.ignore(usersOfType: EndBorrowInst.self).isEmpty { context.erase(instructionIncludingAllUsers: self) return } @@ -52,7 +52,7 @@ extension LoadBorrowInst : Simplifiable, SILCombineSimplifiable { private func tryCombineWithCopy(_ context: SimplifyContext) { let forwardedValue = lookThroughSingleForwardingUses() - guard let singleUser = forwardedValue.uses.ignoreUses(ofType: EndBorrowInst.self).singleUse?.instruction, + guard let singleUser = forwardedValue.uses.ignore(usersOfType: EndBorrowInst.self).singleUse?.instruction, let copy = singleUser as? CopyValueInst, copy.parentBlock == self.parentBlock else { return @@ -81,13 +81,13 @@ private func tryReplaceBorrowWithOwnedOperand(beginBorrow: BeginBorrowInst, _ co private func removeBorrowOfThinFunction(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) { guard let thin2thickFn = beginBorrow.borrowedValue as? ThinToThickFunctionInst, // For simplicity don't go into the trouble of removing reborrow phi arguments. - beginBorrow.uses.filterUses(ofType: BranchInst.self).isEmpty else + beginBorrow.uses.filter(usersOfType: BranchInst.self).isEmpty else { return } // `thin_to_thick_function` has "none" ownership and is compatible with guaranteed values. // Therefore the `begin_borrow` is not needed. - beginBorrow.uses.ignoreUses(ofType: EndBorrowInst.self).replaceAll(with: thin2thickFn, context) + beginBorrow.uses.ignore(usersOfType: EndBorrowInst.self).replaceAll(with: thin2thickFn, context) context.erase(instructionIncludingAllUsers: beginBorrow) } @@ -110,7 +110,7 @@ private func tryReplaceCopy( withCopiedOperandOf beginBorrow: BeginBorrowInst, _ context: SimplifyContext ) -> Bool { - guard let singleUser = forwardedValue.uses.ignoreUses(ofType: EndBorrowInst.self).singleUse?.instruction, + guard let singleUser = forwardedValue.uses.ignore(usersOfType: EndBorrowInst.self).singleUse?.instruction, let copy = singleUser as? CopyValueInst, copy.parentBlock == beginBorrow.parentBlock else { return false @@ -153,7 +153,7 @@ private extension Value { /// ``` /// Returns self if this value has no uses which are ForwardingInstructions. func lookThroughSingleForwardingUses() -> Value { - if let singleUse = uses.ignoreUses(ofType: EndBorrowInst.self).singleUse, + if let singleUse = uses.ignore(usersOfType: EndBorrowInst.self).singleUse, let fwdInst = singleUse.instruction as? (SingleValueInstruction & ForwardingInstruction), fwdInst.canConvertToOwned, fwdInst.isSingleForwardedOperand(singleUse), @@ -165,17 +165,17 @@ private extension Value { } var allUsesCanBeConvertedToOwned: Bool { - let relevantUses = uses.ignoreUses(ofType: EndBorrowInst.self) + let relevantUses = uses.ignore(usersOfType: EndBorrowInst.self) return relevantUses.allSatisfy { $0.canAccept(ownership: .owned) } } func isDestroyed(after nonDestroyUser: Instruction) -> Bool { - return uses.getSingleUser(notOfType: DestroyValueInst.self) == nonDestroyUser && + return uses.singleUser(notOfType: DestroyValueInst.self) == nonDestroyUser && nonDestroyUser.dominates(destroysOf: self) } func replaceAllDestroys(with replacement: Value, _ context: SimplifyContext) { - uses.filterUses(ofType: DestroyValueInst.self).replaceAll(with: replacement, context) + uses.filter(usersOfType: DestroyValueInst.self).replaceAll(with: replacement, context) } } @@ -187,7 +187,7 @@ private extension Instruction { // The value and instruction are in the same block. All uses are dominated by both. return true } - let destroys = value.uses.filterUses(ofType: DestroyValueInst.self) + let destroys = value.uses.filter(usersOfType: DestroyValueInst.self) return destroys.allSatisfy({ $0.instruction.parentBlock == parentBlock}) } } diff --git a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift index af12a0572d8e5..a9a7ba8dc6dcd 100644 --- a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift +++ b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift @@ -406,7 +406,7 @@ private extension Value { // var p = Point(x: 10, y: 20) // let o = UnsafePointer(&p) // Therefore ignore the `end_access` use of a `begin_access`. - let relevantUses = singleUseValue.uses.ignoreDebugUses.ignoreUses(ofType: EndAccessInst.self) + let relevantUses = singleUseValue.uses.ignoreDebugUses.ignore(usersOfType: EndAccessInst.self) guard let use = relevantUses.singleUse else { return nil diff --git a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/StackProtection.swift b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/StackProtection.swift index 586ebe6751486..dd00724b72392 100644 --- a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/StackProtection.swift +++ b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/StackProtection.swift @@ -361,7 +361,7 @@ private struct StackProtectionOptimization { let builder = Builder(after: beginAccess, location: beginAccess.location.asAutoGenerated, context) let temporary = builder.createAllocStack(beginAccess.type) - beginAccess.uses.ignoreUses(ofType: EndAccessInst.self).replaceAll(with: temporary, context) + beginAccess.uses.ignore(usersOfType: EndAccessInst.self).replaceAll(with: temporary, context) for endAccess in beginAccess.endInstructions { let endBuilder = Builder(before: endAccess, location: endAccess.location.asAutoGenerated, context) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index 801c0fbf2ce9a..64c09e45fe389 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -432,7 +432,7 @@ extension Instruction { case .USubOver: // Handle StringObjectOr(tuple_extract(usub_with_overflow(x, offset)), bits) // This pattern appears in UTF8 String literal construction. - if let tei = bi.uses.getSingleUser(ofType: TupleExtractInst.self), + if let tei = bi.uses.singleUser(ofType: TupleExtractInst.self), tei.isResultOfOffsetSubtract { return true } @@ -446,7 +446,7 @@ extension Instruction { // Handle StringObjectOr(tuple_extract(usub_with_overflow(x, offset)), bits) // This pattern appears in UTF8 String literal construction. if tei.isResultOfOffsetSubtract, - let bi = tei.uses.getSingleUser(ofType: BuiltinInst.self), + let bi = tei.uses.singleUser(ofType: BuiltinInst.self), bi.id == .StringObjectOr { return true } diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OwnershipLiveness.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OwnershipLiveness.swift index ba61cf0b440e8..f8166be37ffe0 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OwnershipLiveness.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OwnershipLiveness.swift @@ -717,7 +717,7 @@ extension InteriorUseWalker: AddressUseVisitor { if handleInner(borrowed: sb) == .abortWalk { return .abortWalk } - return sb.uses.filterUses(ofType: EndBorrowInst.self).walk { + return sb.uses.filter(usersOfType: EndBorrowInst.self).walk { useVisitor($0) } case let load as LoadBorrowInst: diff --git a/SwiftCompilerSources/Sources/SIL/Operand.swift b/SwiftCompilerSources/Sources/SIL/Operand.swift index cda27da5f934c..f18497c479b71 100644 --- a/SwiftCompilerSources/Sources/SIL/Operand.swift +++ b/SwiftCompilerSources/Sources/SIL/Operand.swift @@ -143,16 +143,7 @@ extension Sequence where Element == Operand { self.lazy.map { $0.value } } - public var singleUse: Operand? { - var result: Operand? = nil - for op in self { - if result != nil { - return nil - } - result = op - } - return result - } + public var singleUse: Operand? { singleElement } public var isSingleUse: Bool { singleUse != nil } @@ -161,18 +152,14 @@ extension Sequence where Element == Operand { } public var ignoreDebugUses: LazyFilterSequence { - self.lazy.filter { !($0.instruction is DebugValueInst) } + ignore(usersOfType: DebugValueInst.self) } - public func filterUses(ofType: I.Type) -> LazyFilterSequence { + public func filter(usersOfType: I.Type) -> LazyFilterSequence { self.lazy.filter { $0.instruction is I } } - public func filterUsers(ofType: I.Type) -> LazyMapSequence, I> { - self.lazy.filter { $0.instruction is I }.lazy.map { $0.instruction as! I } - } - - public func ignoreUses(ofType: I.Type) -> LazyFilterSequence { + public func ignore(usersOfType: I.Type) -> LazyFilterSequence { self.lazy.filter { !($0.instruction is I) } } @@ -180,12 +167,12 @@ extension Sequence where Element == Operand { self.lazy.filter { !($0.instruction == user) } } - public func getSingleUser(ofType: I.Type) -> I? { - filterUses(ofType: I.self).singleUse?.instruction as? I + public func singleUser(ofType: I.Type) -> I? { + filter(usersOfType: I.self).singleUse?.instruction as? I } - public func getSingleUser(notOfType: I.Type) -> Instruction? { - ignoreUses(ofType: I.self).singleUse?.instruction + public func singleUser(notOfType: I.Type) -> Instruction? { + ignore(usersOfType: I.self).singleUse?.instruction } public var endingLifetime: LazyFilterSequence { diff --git a/SwiftCompilerSources/Sources/SIL/Utilities/BorrowUtils.swift b/SwiftCompilerSources/Sources/SIL/Utilities/BorrowUtils.swift index 187dc228c153b..8b3824bc71164 100644 --- a/SwiftCompilerSources/Sources/SIL/Utilities/BorrowUtils.swift +++ b/SwiftCompilerSources/Sources/SIL/Utilities/BorrowUtils.swift @@ -300,7 +300,7 @@ public enum BorrowingInstruction : CustomStringConvertible, Hashable { extension BorrowingInstruction { private func visitEndBorrows(value: Value, _ context: Context, _ visitor: @escaping (Operand) -> WalkResult) -> WalkResult { - return value.lookThroughBorrowedFromUser.uses.filterUses(ofType: EndBorrowInst.self).walk { + return value.lookThroughBorrowedFromUser.uses.filter(usersOfType: EndBorrowInst.self).walk { visitor($0) } } diff --git a/SwiftCompilerSources/Sources/SIL/Utilities/PhiUpdater.swift b/SwiftCompilerSources/Sources/SIL/Utilities/PhiUpdater.swift index 5029a91136a5e..499c9eaaa6255 100644 --- a/SwiftCompilerSources/Sources/SIL/Utilities/PhiUpdater.swift +++ b/SwiftCompilerSources/Sources/SIL/Utilities/PhiUpdater.swift @@ -137,7 +137,7 @@ private func createEmptyBorrowedFrom(for phi: Phi, _ context: some MutatingConte } let builder = Builder(atBeginOf: phi.value.parentBlock, context) let bfi = builder.createBorrowedFrom(borrowedValue: phi.value, enclosingValues: []) - phi.value.uses.ignoreUses(ofType: BorrowedFromInst.self).replaceAll(with: bfi, context) + phi.value.uses.ignore(usersOfType: BorrowedFromInst.self).replaceAll(with: bfi, context) } /// Replaces a phi with the unique incoming value if all incoming values are the same: