Files
swift-mirror/SwiftCompilerSources/Sources/SIL/Value.swift

343 lines
12 KiB
Swift

//===--- Value.swift - the Value protocol ---------------------------------===//
//
// 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 Basic
import SILBridging
@_semantics("arc.immortal")
public protocol Value : AnyObject, CustomStringConvertible {
var uses: UseList { get }
var type: Type { get }
var ownership: Ownership { get }
/// The instruction which defines the value.
///
/// This is nil if the value is not defined by an instruction, e.g. an `Argument`.
var definingInstruction: Instruction? { get }
/// The block where the value is defined.
var parentBlock: BasicBlock { get }
/// The function where the value lives in.
///
/// It's not legal to get the parentFunction of an instruction in a global initializer.
var parentFunction: Function { get }
/// True if the value has a trivial type.
var hasTrivialType: Bool { get }
/// True if the value has a trivial type which is and does not contain a Builtin.RawPointer.
var hasTrivialNonPointerType: Bool { get }
var isLexical: Bool { get }
}
public enum Ownership {
/// A Value with `unowned` ownership kind is an independent value that
/// has a lifetime that is only guaranteed to last until the next program
/// visible side-effect. To maintain the lifetime of an unowned value, it
/// must be converted to an owned representation via a copy_value.
///
/// Unowned ownership kind occurs mainly along method/function boundaries in
/// between Swift and Objective-C code.
case unowned
/// A Value with `owned` ownership kind is an independent value that has
/// an ownership independent of any other ownership imbued within it. The
/// Value must be paired with a consuming operation that ends the SSA
/// value's lifetime exactly once along all paths through the program.
case owned
/// A Value with `guaranteed` ownership kind is an independent value that
/// is guaranteed to be live over a specific region of the program. This
/// region can come in several forms:
///
/// 1. @guaranteed function argument. This guarantees that a value will
/// outlive a function.
///
/// 2. A shared borrow region. This is a region denoted by a
/// begin_borrow/load_borrow instruction and an end_borrow instruction. The
/// SSA value must not be destroyed or taken inside the borrowed region.
///
/// Any value with guaranteed ownership must be paired with an end_borrow
/// instruction exactly once along any path through the program.
case guaranteed
/// A Value with `none` ownership kind is an independent value outside of
/// the ownership system. It is used to model values that are statically
/// determined to be trivial. This includes trivially typed values as well
/// as trivial cases of non-trivial enums. Naturally `none` can be merged with
/// any ownership, allowing us to naturally model merge and branch
/// points in the SSA graph, where more information about the value is
/// statically available on some control flow paths.
case none
public var hasLifetime: Bool {
switch self {
case .owned, .guaranteed:
return true
case .unowned, .none:
return false
}
}
public init(bridged: BridgedValue.Ownership) {
switch bridged {
case .Unowned: self = .unowned
case .Owned: self = .owned
case .Guaranteed: self = .guaranteed
case .None: self = .none
default:
fatalError("unsupported ownership")
}
}
public var _bridged: BridgedValue.Ownership {
switch self {
case .unowned: return BridgedValue.Ownership.Unowned
case .owned: return BridgedValue.Ownership.Owned
case .guaranteed: return BridgedValue.Ownership.Guaranteed
case .none: return BridgedValue.Ownership.None
}
}
}
extension Value {
public var description: String {
return String(taking: bridged.getDebugDescription())
}
public var uses: UseList { UseList(bridged.getFirstUse()) }
// Default implementation for all values which have a parent block, like instructions and arguments.
public var parentFunction: Function { parentBlock.parentFunction }
public var type: Type { bridged.getType().type }
/// True if the value has a trivial type.
public var hasTrivialType: Bool { type.isTrivial(in: parentFunction) }
/// True if the value has a trivial type which is and does not contain a Builtin.RawPointer.
public var hasTrivialNonPointerType: Bool { type.isTrivialNonPointer(in: parentFunction) }
public var ownership: Ownership {
switch bridged.getOwnership() {
case .Unowned: return .unowned
case .Owned: return .owned
case .Guaranteed: return .guaranteed
case .None: return .none
default:
fatalError("unsupported ownership")
}
}
/// Return true if the object type conforms to Escapable.
///
/// Note: noescape function types conform to Escapable, use mayEscape instead to exclude them.
public var isEscapable: Bool {
type.objectType.isEscapable(in: parentFunction)
}
/// Return true only if this value's lifetime is unconstrained by an outer lifetime. Requires all of the following:
/// - the object type conforms to Escapable
/// - the type is not a noescape function
/// - the value is not the direct result of a partial_apply with a noescape (inout_aliasable) capture.
public var mayEscape: Bool {
if !type.objectType.mayEscape(in: parentFunction) {
return false
}
// A noescape partial_apply has an escaping function type if it has not been promoted to on_stack, but it's value
// still cannot "escape" its captures.
//
// TODO: This would be much more robust if pai.hasNoescapeCapture simply implied !pai.type.isEscapable
if let pai = self as? PartialApplyInst {
return pai.mayEscape
}
return true
}
public var definingInstructionOrTerminator: Instruction? {
if let def = definingInstruction {
return def
} else if let result = TerminatorResult(self) {
return result.terminator
}
return nil
}
public var nextInstruction: Instruction {
if self is Argument {
return parentBlock.instructions.first!
}
// Block terminators do not directly produce values.
return definingInstruction!.next!
}
public var hashable: HashableValue { ObjectIdentifier(self) }
public var bridged: BridgedValue {
BridgedValue(obj: SwiftObject(self as AnyObject))
}
}
public typealias HashableValue = ObjectIdentifier
// We can't make `Value` inherit from `Equatable`, since `Equatable` is a PAT,
// and we do use `Value` existentials around the codebase. Thus functions from
// `Equatable` are declared separately.
public func ==(_ lhs: Value, _ rhs: Value) -> Bool {
return lhs === rhs
}
public func !=(_ lhs: Value, _ rhs: Value) -> Bool {
return !(lhs === rhs)
}
extension CollectionLikeSequence where Element == Value {
public func contains(_ element: Element) -> Bool {
return self.contains { $0 == element }
}
}
/// A projected value, which is defined by the original value and a projection path.
///
/// For example, if the `value` is of type `struct S { var x: Int }` and `path` is `s0`,
/// then the projected value represents field `x` of the original value.
/// An empty path means represents the "whole" original value.
///
public struct ProjectedValue {
public let value: Value
public let path: SmallProjectionPath
}
extension Value {
/// Returns a projected value, defined by this value and `path`.
public func at(_ path: SmallProjectionPath) -> ProjectedValue {
ProjectedValue(value: self, path: path)
}
/// Returns a projected value, defined by this value and path containing a single field of `kind` and `index`.
public func at(_ kind: SmallProjectionPath.FieldKind, index: Int = 0) -> ProjectedValue {
ProjectedValue(value: self, path: SmallProjectionPath(kind, index: index))
}
/// Projects all "contained" addresses of this value.
///
/// If this value is an address, projects all sub-fields of the address, e.g. struct fields.
///
/// If this value is not an address, projects all "interior" pointers of the value:
/// If this value is a class, "interior" pointer means: an address of any stored property of the class instance.
/// If this value is a struct or another value type, "interior" pointers refer to any stored propery addresses of
/// any class references in the struct or value type. For example:
///
/// class C { var x: Int; var y: Int }
/// struct S { var c1: C; var c2: C }
/// let s: S
///
/// `s.allContainedAddresses` refers to `s.c1.x`, `s.c1.y`, `s.c2.x` and `s.c2.y`
///
public var allContainedAddresses: ProjectedValue {
if type.isAddress {
// This is the regular case: the path selects any sub-fields of an address.
return at(SmallProjectionPath(.anyValueFields))
}
if type.isClass {
// If the value is a (non-address) reference it means: all addresses within the class instance.
return at(SmallProjectionPath(.anyValueFields).push(.anyClassField))
}
// Any other non-address value means: all addresses of any referenced class instances within the value.
return at(SmallProjectionPath(.anyValueFields).push(.anyClassField).push(.anyValueFields))
}
}
extension BridgedValue {
public var value: Value {
// Doing the type check in C++ is much faster than a conformance lookup with `as! Value`.
// And it makes a difference because this is a time critical function.
switch getKind() {
case .SingleValueInstruction:
return obj.getAs(SingleValueInstruction.self)
case .Argument:
return obj.getAs(Argument.self)
case .MultipleValueInstructionResult:
return obj.getAs(MultipleValueInstructionResult.self)
case .Undef:
return obj.getAs(Undef.self)
default:
fatalError("unknown Value type")
}
}
}
public final class Undef : Value {
public var definingInstruction: Instruction? { nil }
public var parentFunction: Function { bridged.SILUndef_getParentFunction().function }
public var parentBlock: BasicBlock {
// By convention, undefs are considered to be defined at the entry of the function.
parentFunction.entryBlock
}
/// Undef has not parent function, therefore the default `hasTrivialType` does not work.
/// Return the conservative default in this case.
public var hasTrivialType: Bool { false }
/// Undef has not parent function, therefore the default `hasTrivialNonPointerType` does not work.
/// Return the conservative default in this case.
public var hasTrivialNonPointerType: Bool { false }
public var isLexical: Bool { false }
public static func get(type: Type, _ context: some MutatingContext) -> Undef {
context._bridged.getSILUndef(type.bridged).value as! Undef
}
}
final class PlaceholderValue : Value {
public var definingInstruction: Instruction? { nil }
public var parentBlock: BasicBlock {
fatalError("PlaceholderValue has no defining block")
}
public var isLexical: Bool { false }
public var parentFunction: Function { bridged.PlaceholderValue_getParentFunction().function }
}
extension OptionalBridgedValue {
public var value: Value? { obj.getAs(AnyObject.self) as? Value }
}
extension Optional where Wrapped == Value {
public var bridged: OptionalBridgedValue {
OptionalBridgedValue(obj: self?.bridged.obj)
}
}
//===----------------------------------------------------------------------===//
// Bridging Utilities
//===----------------------------------------------------------------------===//
extension Array where Element == Value {
public func withBridgedValues<T>(_ c: (BridgedValueArray) -> T) -> T {
return self.withUnsafeBufferPointer { bufPtr in
assert(bufPtr.count == self.count)
return bufPtr.withMemoryRebound(to: BridgeValueExistential.self) { valPtr in
return c(BridgedValueArray(base: valPtr.baseAddress, count: self.count))
}
}
}
}