mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
* 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.
374 lines
13 KiB
Swift
374 lines
13 KiB
Swift
//===--- Operand.swift - Instruction operands -----------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2021 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 SILBridging
|
|
|
|
/// An operand of an instruction.
|
|
public struct Operand : CustomStringConvertible, NoReflectionChildren, Equatable {
|
|
public let bridged: BridgedOperand
|
|
|
|
public init(bridged: BridgedOperand) {
|
|
self.bridged = bridged
|
|
}
|
|
|
|
init?(bridged: OptionalBridgedOperand) {
|
|
guard let op = bridged.op else { return nil }
|
|
self.bridged = BridgedOperand(op: op)
|
|
}
|
|
|
|
public var value: Value { bridged.getValue().value }
|
|
|
|
public func set(to value: Value, _ context: some MutatingContext) {
|
|
instruction.setOperand(at: index, to: value, context)
|
|
}
|
|
|
|
public static func ==(lhs: Operand, rhs: Operand) -> Bool {
|
|
return lhs.bridged.op == rhs.bridged.op
|
|
}
|
|
|
|
public var instruction: Instruction {
|
|
return bridged.getUser().instruction
|
|
}
|
|
|
|
public var index: Int { instruction.operands.getIndex(of: self) }
|
|
|
|
/// True if the operand is used to describe a type dependency, but it's not
|
|
/// used as value.
|
|
public var isTypeDependent: Bool { bridged.isTypeDependent() }
|
|
|
|
public var endsLifetime: Bool { bridged.isLifetimeEnding() }
|
|
|
|
public func canAccept(ownership: Ownership) -> Bool { bridged.canAcceptOwnership(ownership._bridged) }
|
|
|
|
public func changeOwnership(from: Ownership, to: Ownership, _ context: some MutatingContext) {
|
|
context.notifyInstructionsChanged()
|
|
bridged.changeOwnership(from._bridged, to._bridged)
|
|
context.notifyInstructionChanged(instruction)
|
|
}
|
|
|
|
public var description: String { "operand #\(index) of \(instruction)" }
|
|
}
|
|
|
|
public struct OperandArray : RandomAccessCollection, CustomReflectable {
|
|
private let base: OptionalBridgedOperand
|
|
public let count: Int
|
|
|
|
init(base: OptionalBridgedOperand, count: Int) {
|
|
self.base = base
|
|
self.count = count
|
|
}
|
|
|
|
init(base: Operand, count: Int) {
|
|
self.base = OptionalBridgedOperand(bridged: base.bridged)
|
|
self.count = count
|
|
}
|
|
|
|
static public var empty: OperandArray {
|
|
OperandArray(base: OptionalBridgedOperand(bridged: nil), count: 0)
|
|
}
|
|
|
|
public var startIndex: Int { return 0 }
|
|
public var endIndex: Int { return count }
|
|
|
|
public subscript(_ index: Int) -> Operand {
|
|
assert(index >= startIndex && index < endIndex)
|
|
return Operand(bridged: base.advancedBy(index))
|
|
}
|
|
|
|
public func getIndex(of operand: Operand) -> Int {
|
|
let idx = base.distanceTo(operand.bridged)
|
|
assert(self[idx].bridged.op == operand.bridged.op)
|
|
return idx
|
|
}
|
|
|
|
public var customMirror: Mirror {
|
|
let c: [Mirror.Child] = map { (label: nil, value: $0.value) }
|
|
return Mirror(self, children: c)
|
|
}
|
|
|
|
/// Returns a sub-array defined by `bounds`.
|
|
///
|
|
/// Note: this does not return a Slice. The first index of the returned array is always 0.
|
|
public subscript(bounds: Range<Int>) -> OperandArray {
|
|
assert(bounds.lowerBound >= startIndex && bounds.upperBound <= endIndex)
|
|
return OperandArray(
|
|
base: OptionalBridgedOperand(op: base.advancedBy(bounds.lowerBound).op),
|
|
count: bounds.upperBound - bounds.lowerBound)
|
|
}
|
|
}
|
|
|
|
public struct UseList : CollectionLikeSequence {
|
|
public struct Iterator : IteratorProtocol {
|
|
var currentOpPtr: OptionalBridgedOperand
|
|
|
|
public mutating func next() -> Operand? {
|
|
if let bridgedOp = currentOpPtr.operand {
|
|
var op = bridgedOp
|
|
// Skip operands of deleted instructions.
|
|
while op.isDeleted() {
|
|
guard let nextOp = op.getNextUse().operand else {
|
|
return nil
|
|
}
|
|
op = nextOp
|
|
}
|
|
currentOpPtr = op.getNextUse();
|
|
return Operand(bridged: op)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private let firstOpPtr: OptionalBridgedOperand
|
|
|
|
init(_ firstOpPtr: OptionalBridgedOperand) {
|
|
self.firstOpPtr = firstOpPtr
|
|
}
|
|
|
|
public func makeIterator() -> Iterator {
|
|
return Iterator(currentOpPtr: firstOpPtr)
|
|
}
|
|
}
|
|
|
|
extension Sequence where Element == Operand {
|
|
public var values: LazyMapSequence<Self, Value> {
|
|
self.lazy.map { $0.value }
|
|
}
|
|
|
|
public var singleUse: Operand? { singleElement }
|
|
|
|
public var isSingleUse: Bool { singleUse != nil }
|
|
|
|
public var ignoreTypeDependence: LazyFilterSequence<Self> {
|
|
self.lazy.filter({!$0.isTypeDependent})
|
|
}
|
|
|
|
public var ignoreDebugUses: LazyFilterSequence<Self> {
|
|
ignore(usersOfType: DebugValueInst.self)
|
|
}
|
|
|
|
public func filter<I: Instruction>(usersOfType: I.Type) -> LazyFilterSequence<Self> {
|
|
self.lazy.filter { $0.instruction is I }
|
|
}
|
|
|
|
public func ignore<I: Instruction>(usersOfType: I.Type) -> LazyFilterSequence<Self> {
|
|
self.lazy.filter { !($0.instruction is I) }
|
|
}
|
|
|
|
public func ignore(user: Instruction) -> LazyFilterSequence<Self> {
|
|
self.lazy.filter { !($0.instruction == user) }
|
|
}
|
|
|
|
public func singleUser<I: Instruction>(ofType: I.Type) -> I? {
|
|
filter(usersOfType: I.self).singleUse?.instruction as? I
|
|
}
|
|
|
|
public func singleUser<I: Instruction>(notOfType: I.Type) -> Instruction? {
|
|
ignore(usersOfType: I.self).singleUse?.instruction
|
|
}
|
|
|
|
public var endingLifetime: LazyFilterSequence<Self> {
|
|
return self.lazy.filter { $0.endsLifetime }
|
|
}
|
|
|
|
public var users: LazyMapSequence<Self, Instruction> {
|
|
return self.lazy.map { $0.instruction }
|
|
}
|
|
|
|
public func users<I: Instruction>(ofType: I.Type) -> LazyMapSequence<LazyFilterSequence<Self>, I> {
|
|
self.lazy.filter{ $0.instruction is I }.lazy.map { $0.instruction as! I }
|
|
}
|
|
|
|
public func replaceAll(with replacement: Value, _ context: some MutatingContext) {
|
|
for use in self {
|
|
use.set(to: replacement, context)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Value {
|
|
public var users: LazyMapSequence<UseList, Instruction> { uses.users }
|
|
}
|
|
|
|
extension Instruction {
|
|
public func isUsing(_ value: Value) -> Bool {
|
|
return operands.contains { $0.value == value }
|
|
}
|
|
}
|
|
|
|
extension Operand {
|
|
/// Return true if this operation will store a full value into this
|
|
/// operand's address.
|
|
public var isAddressInitialization: Bool {
|
|
if !value.type.isAddress {
|
|
return false
|
|
}
|
|
switch instruction {
|
|
case is StoringInstruction:
|
|
return true
|
|
case let srcDestInst as SourceDestAddrInstruction
|
|
where srcDestInst.destinationOperand == self:
|
|
return true
|
|
case let apply as FullApplySite:
|
|
return apply.isIndirectResult(operand: self)
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Operand {
|
|
/// A scope ending use is a consuming use for normal borrow scopes, but it also applies to intructions that end the
|
|
/// scope of an address (end_access) or a token (end_apply, abort_apply),
|
|
public var isScopeEndingUse: Bool {
|
|
switch instruction {
|
|
case is EndBorrowInst, is EndAccessInst, is EndApplyInst, is AbortApplyInst:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
extension OptionalBridgedOperand {
|
|
init(bridged: BridgedOperand?) {
|
|
self = OptionalBridgedOperand(op: bridged?.op)
|
|
}
|
|
var operand: BridgedOperand? {
|
|
if let op = op {
|
|
return BridgedOperand(op: op)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
/// Categorize all uses in terms of their ownership effect. Implies ownership and lifetime constraints.
|
|
public enum OperandOwnership {
|
|
/// Operands that do not use the value. They only represent a dependence on a dominating definition and do not require liveness. (type-dependent operands)
|
|
case nonUse
|
|
|
|
/// Uses that can only handle trivial values. The operand value must have None ownership. These uses require liveness but are otherwise unverified.
|
|
case trivialUse
|
|
|
|
/// Use the value only for the duration of the operation, which may have side effects. (single-instruction apply with @guaranteed argument)
|
|
case instantaneousUse
|
|
|
|
/// Use a value without requiring or propagating ownership. The operation may not have side-effects that could affect ownership. This is limited to a small number of operations that are allowed to take Unowned values. (copy_value, single-instruction apply with @unowned argument))
|
|
case unownedInstantaneousUse
|
|
|
|
/// Forwarding instruction with an Unowned result. Its operands may have any ownership.
|
|
case forwardingUnowned
|
|
|
|
/// Escape a pointer into a value which cannot be tracked or verified.
|
|
///
|
|
/// PointerEscape operands indicate a SIL deficiency to sufficiently model dependencies. They never arise from user-level escapes.
|
|
case pointerEscape
|
|
|
|
/// Bitwise escape. Escapes the nontrivial contents of the value. OSSA does not enforce the lifetime of the escaping bits. The programmer must explicitly force lifetime extension. (ref_to_unowned, unchecked_trivial_bitcast)
|
|
case bitwiseEscape
|
|
|
|
/// Borrow. Propagates the owned or guaranteed value within a scope, without ending its lifetime. (begin_borrow, begin_apply with @guaranteed argument)
|
|
case borrow
|
|
|
|
/// Destroying Consume. Destroys the owned value immediately. (store, destroy, @owned destructure).
|
|
case destroyingConsume
|
|
|
|
/// Forwarding Consume. Consumes the owned value indirectly via a move. (br, destructure, tuple, struct, cast, switch).
|
|
case forwardingConsume
|
|
|
|
/// Interior Pointer. Propagates a trivial value (e.g. address, pointer, or no-escape closure) that depends on the guaranteed value within the base's borrow scope. The verifier checks that all uses of the trivial
|
|
/// value are in scope. (ref_element_addr, open_existential_box)
|
|
case interiorPointer
|
|
|
|
/// Any Interior Pointer. An interior pointer that allows any operand ownership. This will be removed as soon as SIL
|
|
/// migrates away from extraneous borrow scopes.
|
|
case anyInteriorPointer
|
|
|
|
/// Forwarded Borrow. Propagates the guaranteed value within the base's borrow scope. (tuple_extract, struct_extract, cast, switch)
|
|
case guaranteedForwarding
|
|
|
|
/// End Borrow. End the borrow scope opened directly by the operand. The operand must be a begin_borrow, begin_apply, or function argument. (end_borrow, end_apply)
|
|
case endBorrow
|
|
|
|
/// Reborrow. Ends the borrow scope opened directly by the operand and begins one or multiple disjoint borrow scopes. If a forwarded value is reborrowed, then its base must also be reborrowed at the same point. (br, FIXME: should also include destructure, tuple, struct)
|
|
case reborrow
|
|
|
|
public var endsLifetime: Bool {
|
|
switch self {
|
|
case .nonUse, .trivialUse, .instantaneousUse, .unownedInstantaneousUse,
|
|
.forwardingUnowned, .pointerEscape, .bitwiseEscape, .borrow,
|
|
.interiorPointer, .anyInteriorPointer, .guaranteedForwarding:
|
|
return false
|
|
case .destroyingConsume, .forwardingConsume, .endBorrow, .reborrow:
|
|
return true
|
|
}
|
|
}
|
|
|
|
public var _bridged: BridgedOperand.OperandOwnership {
|
|
switch self {
|
|
case .nonUse:
|
|
return BridgedOperand.OperandOwnership.NonUse
|
|
case .trivialUse:
|
|
return BridgedOperand.OperandOwnership.TrivialUse
|
|
case .instantaneousUse:
|
|
return BridgedOperand.OperandOwnership.InstantaneousUse
|
|
case .unownedInstantaneousUse:
|
|
return BridgedOperand.OperandOwnership.UnownedInstantaneousUse
|
|
case .forwardingUnowned:
|
|
return BridgedOperand.OperandOwnership.ForwardingUnowned
|
|
case .pointerEscape:
|
|
return BridgedOperand.OperandOwnership.PointerEscape
|
|
case .bitwiseEscape:
|
|
return BridgedOperand.OperandOwnership.BitwiseEscape
|
|
case .borrow:
|
|
return BridgedOperand.OperandOwnership.Borrow
|
|
case .destroyingConsume:
|
|
return BridgedOperand.OperandOwnership.DestroyingConsume
|
|
case .forwardingConsume:
|
|
return BridgedOperand.OperandOwnership.ForwardingConsume
|
|
case .interiorPointer:
|
|
return BridgedOperand.OperandOwnership.InteriorPointer
|
|
case .anyInteriorPointer:
|
|
return BridgedOperand.OperandOwnership.AnyInteriorPointer
|
|
case .guaranteedForwarding:
|
|
return BridgedOperand.OperandOwnership.GuaranteedForwarding
|
|
case .endBorrow:
|
|
return BridgedOperand.OperandOwnership.EndBorrow
|
|
case .reborrow:
|
|
return BridgedOperand.OperandOwnership.Reborrow
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Operand {
|
|
public var ownership: OperandOwnership {
|
|
switch bridged.getOperandOwnership() {
|
|
case .NonUse: return .nonUse
|
|
case .TrivialUse: return .trivialUse
|
|
case .InstantaneousUse: return .instantaneousUse
|
|
case .UnownedInstantaneousUse: return .unownedInstantaneousUse
|
|
case .ForwardingUnowned: return .forwardingUnowned
|
|
case .PointerEscape: return .pointerEscape
|
|
case .BitwiseEscape: return .bitwiseEscape
|
|
case .Borrow: return .borrow
|
|
case .DestroyingConsume: return .destroyingConsume
|
|
case .ForwardingConsume: return .forwardingConsume
|
|
case .InteriorPointer: return .interiorPointer
|
|
case .AnyInteriorPointer: return .anyInteriorPointer
|
|
case .GuaranteedForwarding: return .guaranteedForwarding
|
|
case .EndBorrow: return .endBorrow
|
|
case .Reborrow: return .reborrow
|
|
default:
|
|
fatalError("unsupported operand ownership")
|
|
}
|
|
}
|
|
}
|