1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
|
//===--- 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 {
fileprivate 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 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 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
}
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 returnd 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 var values: LazyMapSequence<LazySequence<OperandArray>.Elements, Value> {
self.lazy.map { $0.value }
}
}
public struct UseList : CollectionLikeSequence {
public struct Iterator : IteratorProtocol {
var currentOpPtr: OptionalBridgedOperand
public mutating func next() -> Operand? {
if let op = currentOpPtr.operand {
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 singleUse: Operand? {
var result: Operand? = nil
for op in self {
if result != nil {
return nil
}
result = op
}
return result
}
public var isSingleUse: Bool { singleUse != nil }
public var ignoreTypeDependence: LazyFilterSequence<Self> {
self.lazy.filter({!$0.isTypeDependent})
}
public var ignoreDebugUses: LazyFilterSequence<Self> {
self.lazy.filter { !($0.instruction is DebugValueInst) }
}
public func filterUsers<I: Instruction>(ofType: I.Type) -> LazyFilterSequence<Self> {
self.lazy.filter { $0.instruction is I }
}
public func ignoreUsers<I: Instruction>(ofType: I.Type) -> LazyFilterSequence<Self> {
self.lazy.filter { !($0.instruction is I) }
}
public func getSingleUser<I: Instruction>(ofType: I.Type) -> I? {
filterUsers(ofType: I.self).singleUse?.instruction as? I
}
public func getSingleUser<I: Instruction>(notOfType: I.Type) -> Instruction? {
ignoreUsers(ofType: I.self).singleUse?.instruction
}
public var endingLifetime: LazyFilterSequence<Self> {
return self.lazy.filter { $0.endsLifetime }
}
}
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 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 suffuciently 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
/// 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, .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 .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 .GuaranteedForwarding: return .guaranteedForwarding
case .EndBorrow: return .endBorrow
case .Reborrow: return .reborrow
default:
fatalError("unsupported operand ownership")
}
}
}
|