|
| 1 | +//===--- ForwardingInstruction.swift - forwarding instruction protocols ---===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | + |
| 13 | +import SILBridging |
| 14 | + |
| 15 | +/// An instruction that forwards ownership from operands to results. |
| 16 | +public protocol ForwardingInstruction : Instruction { |
| 17 | + var singleForwardedOperand: Operand? { get } |
| 18 | + |
| 19 | + /// Return true if the forwarded value has the same representation. If true, then the result can be mapped to the same storage without a move or copy. |
| 20 | + var preservesRepresentation: Bool { get } |
| 21 | +} |
| 22 | + |
| 23 | +extension ForwardingInstruction { |
| 24 | + public var forwardedOperands: OperandArray { |
| 25 | + // Some instructions have multiple real operands but only forward one. |
| 26 | + if let singleForwardingOp = singleForwardedOperand { |
| 27 | + return OperandArray(base: singleForwardingOp, count: 1) |
| 28 | + } |
| 29 | + // All others forward all operands (for enum, this may be zero operands). |
| 30 | + return operands |
| 31 | + } |
| 32 | + |
| 33 | + public var forwardedResults: ForwardedResults { |
| 34 | + ForwardedResults(inst: self) |
| 35 | + } |
| 36 | + |
| 37 | + /// If forwarding ownership is owned, then the instruction moves an owned operand to its result, ending its lifetime. If forwarding ownership is guaranteed, then the instruction propagates the lifetime of its borrows operand through its result. |
| 38 | + /// |
| 39 | + /// The resulting forwarded value's ownership (Value.ownership) is not identical to the instruction's forwarding ownership property. It differs when the result is trivial type. e.g. an owned or guaranteed value can be cast to a trivial type using owned or guaranteed forwarding. |
| 40 | + public var forwardingOwnership: Ownership { |
| 41 | + Ownership(bridged: bridged.ForwardingInst_forwardingOwnership()) |
| 42 | + } |
| 43 | + |
| 44 | + /// A forwarding instruction preserves reference counts if it has a dynamically non-trivial result in which all references are forwarded from the operand. |
| 45 | + /// |
| 46 | + /// A cast can only forward guaranteed values if it preserves reference counts. Such casts cannot release any references within their operand's value and cannot retain any references owned by their result. |
| 47 | + public var preservesReferenceCounts: Bool { |
| 48 | + bridged.ForwardingInst_preservesOwnership() |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +// An instruction that forwards a single value to a single result. |
| 53 | +// |
| 54 | +// For legacy reasons, some ForwardingInstructions that fit the SingleValueInstruction and UnaryInstruction requirements are not considered ConversionInstructions because certain routines do not want to see through them (InitExistentialValueInst, InitExistentialValueInst, OpenExistentialValueInst, OpenExistentialValueInst). This most likely has to do with type-dependent operands, although any ConversionInstruction should support type-dependent operands. |
| 55 | +public protocol ConversionInstruction : SingleValueInstruction, |
| 56 | + UnaryInstruction, |
| 57 | + ForwardingInstruction |
| 58 | +{} |
| 59 | + |
| 60 | +extension Value { |
| 61 | + // If this value is produced by a ForwardingInstruction, return that instruction. This is convenient for following the forwarded value chain. |
| 62 | + // Unlike definingInstruction, a value's forwardingInstruction is not necessarily a valid insertion point. |
| 63 | + public var forwardingInstruction: ForwardingInstruction? { |
| 64 | + if let inst = definingInstruction { |
| 65 | + return inst as? ForwardingInstruction |
| 66 | + } |
| 67 | + if let termResult = TerminatorResult(self) { |
| 68 | + return termResult.terminator as? ForwardingInstruction |
| 69 | + } |
| 70 | + return nil |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +//===----------------------------------------------------------------------===// |
| 75 | +// singleForwardedOperand |
| 76 | +//===----------------------------------------------------------------------===// |
| 77 | + |
| 78 | +extension ForwardingInstruction { |
| 79 | + // See ForwardingOperation::getSingleForwardingOperand(). |
| 80 | + public var singleForwardedOperand: Operand? { |
| 81 | + let definedOps = self.definedOperands |
| 82 | + assert(definedOps.count == 1); |
| 83 | + return definedOps[0]; |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +extension ConversionInstruction { |
| 88 | + public var singleForwardedOperand: Operand? { operand } |
| 89 | +} |
| 90 | + |
| 91 | +extension EnumInst { |
| 92 | + public var singleForwardedOperand: Operand? { |
| 93 | + return operand // nil for an enum with no payload |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +// ----------------------------------------------------------------------------- |
| 98 | +// Instructions with multiple forwarded operands have nil singleForwardedOperand. |
| 99 | + |
| 100 | +extension StructInst { |
| 101 | + public var singleForwardedOperand: Operand? { nil } |
| 102 | +} |
| 103 | + |
| 104 | +extension TupleInst { |
| 105 | + public var singleForwardedOperand: Operand? { nil } |
| 106 | +} |
| 107 | + |
| 108 | +extension LinearFunctionInst { |
| 109 | + public var singleForwardedOperand: Operand? { nil } |
| 110 | +} |
| 111 | + |
| 112 | +extension DifferentiableFunctionInst { |
| 113 | + public var singleForwardedOperand: Operand? { nil } |
| 114 | +} |
| 115 | + |
| 116 | +// ----------------------------------------------------------------------------- |
| 117 | +// Instructions with a singleForwardedOperand and additional operands. |
| 118 | + |
| 119 | +extension MarkDependenceInst { |
| 120 | + public var singleForwardedOperand: Operand { |
| 121 | + return valueOperand |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +extension RefToBridgeObjectInst { |
| 126 | + public var singleForwardedOperand: Operand { |
| 127 | + return convertedOperand |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +extension TuplePackExtractInst { |
| 132 | + public var singleForwardedOperand: Operand { |
| 133 | + return tupleOperand |
| 134 | + } |
| 135 | +} |
| 136 | + |
| 137 | +extension SelectEnumInst { |
| 138 | + public var singleForwardedOperand: Operand { |
| 139 | + return enumOperand |
| 140 | + } |
| 141 | +} |
| 142 | + |
| 143 | +//===----------------------------------------------------------------------===// |
| 144 | +// forwardedResults |
| 145 | +//===----------------------------------------------------------------------===// |
| 146 | + |
| 147 | +public struct ForwardedResults : Collection { |
| 148 | + private let inst: Instruction |
| 149 | + private let maxResults: Int |
| 150 | + |
| 151 | + fileprivate init(inst: ForwardingInstruction) { |
| 152 | + self.inst = inst |
| 153 | + if let ti = inst as? TermInst { |
| 154 | + self.maxResults = ti.successors.count |
| 155 | + } else { |
| 156 | + self.maxResults = inst.results.count |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + public var startIndex: Int { skipEmptyResults(at: 0) } |
| 161 | + |
| 162 | + public var endIndex: Int { maxResults } |
| 163 | + |
| 164 | + public func index(after index: Int) -> Int { |
| 165 | + return skipEmptyResults(at: index + 1) |
| 166 | + } |
| 167 | + |
| 168 | + public subscript(_ index: Int) -> Value { |
| 169 | + if let ti = inst as? TermInst { |
| 170 | + return getTerminatorResult(termInst: ti, index: index)! |
| 171 | + } |
| 172 | + return inst.results[index] |
| 173 | + } |
| 174 | + |
| 175 | + private func skipEmptyResults(at index: Int) -> Int { |
| 176 | + guard let ti = inst as? TermInst else { return index } |
| 177 | + var next = index |
| 178 | + while next != endIndex { |
| 179 | + if getTerminatorResult(termInst: ti, index: next) != nil { break } |
| 180 | + next += 1 |
| 181 | + } |
| 182 | + return next |
| 183 | + } |
| 184 | + |
| 185 | + // Forwarding terminators have a zero or one result per successor. |
| 186 | + private func getTerminatorResult(termInst: TermInst, |
| 187 | + index: Int) -> Argument? { |
| 188 | + let succ = termInst.successors[index] |
| 189 | + guard succ.arguments.count == 1 else { |
| 190 | + // The default enum payload may be empty. |
| 191 | + assert(succ.arguments.count == 0, "terminator must forward a single value") |
| 192 | + return nil |
| 193 | + } |
| 194 | + return succ.arguments[0] |
| 195 | + } |
| 196 | +} |
| 197 | + |
| 198 | +//===----------------------------------------------------------------------===// |
| 199 | +// preservesRepresentation |
| 200 | +//===----------------------------------------------------------------------===// |
| 201 | + |
| 202 | +extension ForwardingInstruction { |
| 203 | + // Conservatively assume that a conversion changes representation. |
| 204 | + // Operations can be added as needed to participate in SIL opaque values. |
| 205 | + // See ForwardingOperation::hasSameRepresentation(). |
| 206 | + public var preservesRepresentation: Bool { false } |
| 207 | +} |
| 208 | + |
| 209 | +extension ConvertFunctionInst { |
| 210 | + public var preservesRepresentation: Bool { true } |
| 211 | +} |
| 212 | + |
| 213 | +extension DestructureTupleInst { |
| 214 | + public var preservesRepresentation: Bool { true } |
| 215 | +} |
| 216 | + |
| 217 | +extension DestructureStructInst { |
| 218 | + public var preservesRepresentation: Bool { true } |
| 219 | +} |
| 220 | + |
| 221 | +extension InitExistentialRefInst { |
| 222 | + public var preservesRepresentation: Bool { true } |
| 223 | +} |
| 224 | + |
| 225 | +extension ObjectInst { |
| 226 | + public var preservesRepresentation: Bool { true } |
| 227 | +} |
| 228 | + |
| 229 | +extension OpenExistentialBoxValueInst { |
| 230 | + public var preservesRepresentation: Bool { true } |
| 231 | +} |
| 232 | + |
| 233 | +extension OpenExistentialRefInst { |
| 234 | + public var preservesRepresentation: Bool { true } |
| 235 | +} |
| 236 | + |
| 237 | +extension OpenExistentialValueInst { |
| 238 | + public var preservesRepresentation: Bool { true } |
| 239 | +} |
| 240 | + |
| 241 | +extension MarkUnresolvedNonCopyableValueInst { |
| 242 | + public var preservesRepresentation: Bool { true } |
| 243 | +} |
| 244 | + |
| 245 | +extension MarkUninitializedInst { |
| 246 | + public var preservesRepresentation: Bool { true } |
| 247 | +} |
| 248 | + |
| 249 | +extension SelectEnumInst { |
| 250 | + public var preservesRepresentation: Bool { true } |
| 251 | +} |
| 252 | + |
| 253 | +extension StructExtractInst { |
| 254 | + public var preservesRepresentation: Bool { true } |
| 255 | +} |
| 256 | + |
| 257 | +extension TupleExtractInst { |
| 258 | + public var preservesRepresentation: Bool { true } |
| 259 | +} |
| 260 | + |
| 261 | +extension TuplePackExtractInst { |
| 262 | + public var preservesRepresentation: Bool { true } |
| 263 | +} |
0 commit comments