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 eb1d5f484c.
This commit is contained in:
Erik Eckstein
2025-10-16 10:12:33 +02:00
parent 2fceb9a41d
commit 610539a85f
14 changed files with 34 additions and 47 deletions

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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:

View File

@@ -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})
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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:

View File

@@ -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> {
self.lazy.filter { !($0.instruction is DebugValueInst) }
ignore(usersOfType: DebugValueInst.self)
}
public func filterUses<I: Instruction>(ofType: I.Type) -> LazyFilterSequence<Self> {
public func filter<I: Instruction>(usersOfType: I.Type) -> LazyFilterSequence<Self> {
self.lazy.filter { $0.instruction is I }
}
public func filterUsers<I: Instruction>(ofType: I.Type) -> LazyMapSequence<LazyFilterSequence<Self>, I> {
self.lazy.filter { $0.instruction is I }.lazy.map { $0.instruction as! I }
}
public func ignoreUses<I: Instruction>(ofType: I.Type) -> LazyFilterSequence<Self> {
public func ignore<I: Instruction>(usersOfType: I.Type) -> LazyFilterSequence<Self> {
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<I: Instruction>(ofType: I.Type) -> I? {
filterUses(ofType: I.self).singleUse?.instruction as? I
public func singleUser<I: Instruction>(ofType: I.Type) -> I? {
filter(usersOfType: I.self).singleUse?.instruction as? I
}
public func getSingleUser<I: Instruction>(notOfType: I.Type) -> Instruction? {
ignoreUses(ofType: I.self).singleUse?.instruction
public func singleUser<I: Instruction>(notOfType: I.Type) -> Instruction? {
ignore(usersOfType: I.self).singleUse?.instruction
}
public var endingLifetime: LazyFilterSequence<Self> {

View File

@@ -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)
}
}

View File

@@ -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: