blob: 20ecc8cdea9a1a18f468de592bc2482649deca68 [file] [log] [blame]
//===--- ManagedValue.h - Exploded RValue Representation --------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
//
// A storage structure for holding a destructured rvalue with an optional
// cleanup(s).
// Ownership of the rvalue can be "forwarded" to disable the associated
// cleanup(s).
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_LOWERING_MANAGEDVALUE_H
#define SWIFT_LOWERING_MANAGEDVALUE_H
#include "Cleanup.h"
#include "llvm/ADT/PointerIntPair.h"
#include "swift/SIL/Consumption.h"
#include "swift/SIL/SILValue.h"
namespace swift {
enum class CastConsumptionKind : unsigned char;
namespace Lowering {
class Initialization;
class SILGenFunction;
/// ManagedValue - represents a singular SIL value and an optional cleanup.
/// Ownership of the ManagedValue can be "forwarded" to disable its cleanup when
/// the rvalue is consumed. A ManagedValue can also represent an LValue used as
/// a value, such as an inout function argument, and can be null.
///
/// Interesting relevant cases include:
/// LValue: the SILValue will always have an isAddress() SILType. LValues
/// never have an associated cleanup.
/// RValue, isAddress() type: an address-only RValue.
/// RValue, !isAddress() type: a loadable RValue.
/// "InContext": Represented with the lvalue flag set but with no SILValue,
/// this represents a value that was emitted directly into an
/// initialization stored by an SGFContext.
///
/// The RValue cases may or may not have a cleanup associated with the value. A
/// cleanup is associated with +1 values of non-trivial type and +0 values of
/// non-trivial type.
///
class ManagedValue {
/// The value (or address of an address-only value) being managed, and
/// whether it represents an lvalue. InContext is represented with the lvalue
/// flag set but with a null SILValue.
llvm::PointerIntPair<SILValue, 1, bool> valueAndFlag;
/// A handle to the cleanup that destroys this value, or
/// CleanupHandle::invalid() if the value has no cleanup.
CleanupHandle cleanup;
explicit ManagedValue(SILValue value, bool isLValue, CleanupHandle cleanup)
: valueAndFlag(value, isLValue), cleanup(cleanup) {
}
public:
ManagedValue() = default;
/// Create a managed value for a +1 rvalue.
///
/// Please do not introduce new uses of this method! Instead use one of the
/// static constructors below.
ManagedValue(SILValue value, CleanupHandle cleanup)
: valueAndFlag(value, false), cleanup(cleanup) {
assert(value && "No value specified?!");
assert((!getType().isObject() ||
value.getOwnershipKind() != ValueOwnershipKind::Trivial ||
!hasCleanup()) &&
"Objects with trivial ownership should never have a cleanup");
}
/// Create a managed value for a +0 rvalue.
///
/// Please do not introduce new uses of this method! Instead use one of the
/// static constructors below!
static ManagedValue forUnmanaged(SILValue value) {
assert(value && "No value specified");
return ManagedValue(value, false, CleanupHandle::invalid());
}
/// Create a managed value for a +1 rvalue object.
static ManagedValue forOwnedObjectRValue(SILValue value,
CleanupHandle cleanup) {
assert(value && "No value specified");
assert(value->getType().isObject() &&
"Expected borrowed rvalues to be objects");
assert(value.getOwnershipKind() != ValueOwnershipKind::Trivial);
return ManagedValue(value, false, cleanup);
}
/// Create a managed value for a +1 rvalue address.
///
/// From a high level perspective, this consists of a temporary buffer.
static ManagedValue forOwnedAddressRValue(SILValue value,
CleanupHandle cleanup) {
assert(value && "No value specified");
assert(value->getType().isAddress() && "Expected value to be an address");
assert(value.getOwnershipKind() == ValueOwnershipKind::Trivial &&
"Addresses always have trivial ownership");
return ManagedValue(value, false, cleanup);
}
/// Create a managed value for a +1 non-trivial rvalue.
static ManagedValue forOwnedRValue(SILValue value, CleanupHandle cleanup) {
if (value->getType().isAddress())
return ManagedValue::forOwnedAddressRValue(value, cleanup);
return ManagedValue::forOwnedObjectRValue(value, cleanup);
}
/// Create a managed value for a +0 borrowed non-trivial rvalue object.
static ManagedValue
forBorrowedObjectRValue(SILValue value) {
assert(value && "No value specified");
assert(value->getType().isObject() &&
"Expected borrowed rvalues to be objects");
assert(value.getOwnershipKind() != ValueOwnershipKind::Trivial);
return ManagedValue(value, false, CleanupHandle::invalid());
}
/// Create a managed value for a +0 borrowed non-trivial rvalue address.
static ManagedValue
forBorrowedAddressRValue(SILValue value) {
assert(value && "No value specified");
assert(value->getType().isAddress() && "Expected value to be an address");
assert(value.getOwnershipKind() == ValueOwnershipKind::Trivial &&
"Addresses always have trivial ownership");
return ManagedValue(value, false, CleanupHandle::invalid());
}
/// Create a managed value for a +0 guaranteed rvalue.
static ManagedValue
forBorrowedRValue(SILValue value) {
if (value->getType().isAddress())
return ManagedValue::forBorrowedAddressRValue(value);
return ManagedValue::forBorrowedObjectRValue(value);
}
/// Create a managed value for a +0 trivial object rvalue.
static ManagedValue forTrivialObjectRValue(SILValue value) {
assert(value->getType().isObject() && "Expected an object");
assert(value.getOwnershipKind() == ValueOwnershipKind::Trivial);
return ManagedValue(value, false, CleanupHandle::invalid());
}
/// Create a managed value for a +0 trivial address rvalue.
static ManagedValue forTrivialAddressRValue(SILValue value) {
assert(value->getType().isAddress() && "Expected an address");
assert(value.getOwnershipKind() == ValueOwnershipKind::Trivial);
return ManagedValue(value, false, CleanupHandle::invalid());
}
/// Create a managed value for a +0 trivial rvalue.
static ManagedValue forTrivialRValue(SILValue value) {
if (value->getType().isObject())
return ManagedValue::forTrivialObjectRValue(value);
return ManagedValue::forTrivialAddressRValue(value);
}
/// Create a managed value for an l-value.
static ManagedValue forLValue(SILValue value) {
assert(value && "No value specified");
assert(value->getType().isAddress() &&
"lvalues always have isAddress() type");
return ManagedValue(value, true, CleanupHandle::invalid());
}
/// Create a managed value that indicates that the value you're looking for
/// got stored into an initialization specified by an SGFContext, instead of
/// being represented by this ManagedValue.
static ManagedValue forInContext() {
return ManagedValue(SILValue(), true, CleanupHandle::invalid());
}
bool isLValue() const {
return valueAndFlag.getInt() && valueAndFlag.getPointer();
}
bool isInContext() const {
return valueAndFlag.getInt() && !valueAndFlag.getPointer();
}
/// Return true if this is an +0 rvalue, or has trivial type.
bool isPlusZeroRValueOrTrivial() const {
// If this is an lvalue or isInContext() then it is not an RValue.
if (isLValue() || isInContext()) return false;
// If this has a cleanup attached, then it is +1 rvalue. If not, it is
// either +0 or trivial (in which case +0 vs +1 doesn't matter).
return !hasCleanup();
}
SILValue getLValueAddress() const {
assert(isLValue() && "This isn't an lvalue");
return getValue();
}
SILValue getUnmanagedValue() const {
assert(!hasCleanup());
return getValue();
}
SILValue getValue() const { return valueAndFlag.getPointer(); }
SILType getType() const { return getValue()->getType(); }
ValueOwnershipKind getOwnershipKind() const {
return getValue().getOwnershipKind();
}
/// Transform the given ManagedValue, replacing the underlying value, but
/// keeping the same cleanup.
///
/// For owned values, this is equivalent to forwarding the cleanup and
/// creating a new cleanup of the same type on the new value. This is useful
/// for forwarding sequences.
///
/// For all other values, it is a move.
ManagedValue transform(SILValue newValue) && {
assert(getValue().getOwnershipKind() == newValue.getOwnershipKind() &&
"New value and old value must have the same ownership kind");
ManagedValue M(newValue, isLValue(), getCleanup());
*this = ManagedValue();
return M;
}
/// Emit a copy of this value with independent ownership.
ManagedValue copy(SILGenFunction &SGF, SILLocation loc) const;
/// Emit a copy of this value with independent ownership into the current
/// formal evaluation scope.
ManagedValue formalAccessCopy(SILGenFunction &SGF, SILLocation loc);
/// Store a copy of this value with independent ownership into the given
/// uninitialized address.
void copyInto(SILGenFunction &SGF, SILValue dest, SILLocation loc);
/// This is the same operation as 'copy', but works on +0 values that don't
/// have cleanups. It returns a +1 value with one.
ManagedValue copyUnmanaged(SILGenFunction &SGF, SILLocation loc);
/// This is the same operation as 'formalAccessCopy', but works on +0 values
/// that don't have cleanups. It returns a +1 value with one.
ManagedValue formalAccessCopyUnmanaged(SILGenFunction &SGF, SILLocation loc);
bool hasCleanup() const { return cleanup.isValid(); }
CleanupHandle getCleanup() const { return cleanup; }
/// Return a "borrowed" version of this value.
///
/// An l-value is borrowed as itself. A +1 r-value is borrowed as a
/// +0 r-value, with the assumption that the original ManagedValue
/// will not be forwarded until the borrowed value is fully used.
ManagedValue borrow(SILGenFunction &SGF, SILLocation loc) const;
/// Return a formally evaluated "borrowed" version of this value.
ManagedValue formalAccessBorrow(SILGenFunction &SGF, SILLocation loc) const;
ManagedValue unmanagedBorrow() const {
return isLValue() ? *this : ManagedValue::forUnmanaged(getValue());
}
/// If this managed value is a plus one value, return *this. If this is a plus
/// zero value, return a copy instead.
ManagedValue ensurePlusOne(SILGenFunction &SGF, SILLocation loc) const;
/// Given a scalar value, materialize it into memory with the
/// exact same level of cleanup it had before.
ManagedValue materialize(SILGenFunction &SGF, SILLocation loc) const;
/// Disable the cleanup for this value.
void forwardCleanup(SILGenFunction &SGF) const;
/// Forward this value, deactivating the cleanup and returning the
/// underlying value.
SILValue forward(SILGenFunction &SGF) const;
/// Forward this value into memory by storing it to the given address.
///
/// \param SGF - The SILGenFunction.
/// \param loc - the AST location to associate with emitted instructions.
/// \param address - the address to assign to.
void forwardInto(SILGenFunction &SGF, SILLocation loc, SILValue address);
/// Forward this value into the given initialization.
///
/// \param SGF - The SILGenFunction.
/// \param loc - the AST location to associate with emitted instructions.
/// \param dest - the destination to forward into
void forwardInto(SILGenFunction &SGF, SILLocation loc, Initialization *dest);
/// Assign this value into memory, destroying the existing
/// value at the destination address.
///
/// \param SGF - The SILGenFunction.
/// \param loc - the AST location to associate with emitted instructions.
/// \param address - the address to assign to.
void assignInto(SILGenFunction &SGF, SILLocation loc, SILValue address);
explicit operator bool() const {
// "InContext" is not considered false.
return bool(getValue()) || valueAndFlag.getInt();
}
void dump() const;
void dump(raw_ostream &os, unsigned indent = 0) const;
void print(raw_ostream &os) const;
};
/// A ManagedValue which may not be intended to be consumed.
///
/// The invariant is that the cleanup on a ManagedValue that's not
/// meant to be consumed should be free to clear.
///
/// Code which gets a ManagedValue from a ConsumableManagedValue
/// must be careful before handing the MV off to an API. Many
/// SILGen APIs expect that an MV is +1, but ConsumableManagedValue
/// often traffics in borrowed values. A value is only +1 if
/// the associated consumption is TakeAlways, but conditional
/// operation should turn TakeOnSuccess consumptions into TakeAlways
/// consumptions on their success path.
class ConsumableManagedValue {
ManagedValue Value;
CastConsumptionKind FinalConsumption;
public:
/// Create an invalid CMV.
ConsumableManagedValue() = default;
/// Create a CMV with a specific value and consumption rule.
/*implicit*/ ConsumableManagedValue(ManagedValue value,
CastConsumptionKind finalConsumption)
: Value(value), FinalConsumption(finalConsumption) {}
/// Create a CMV for a value of trivial type.
static ConsumableManagedValue forUnmanaged(SILValue value) {
return { ManagedValue::forUnmanaged(value),
CastConsumptionKind::TakeAlways };
}
/// Create a CMV for an owned value.
static ConsumableManagedValue forOwned(ManagedValue value) {
return { value, CastConsumptionKind::TakeAlways };
}
/// Has this been filled in with meaningful data?
bool isValid() const { return (bool) Value; }
bool isOwned() const {
assert(isValid());
return FinalConsumption == CastConsumptionKind::TakeAlways;
}
/// Return true if there's a cleanup associated with this value.
bool hasCleanup() const { return Value.hasCleanup(); }
CleanupHandle getCleanup() const { return Value.getCleanup(); }
SILType getType() const { return Value.getType(); }
SILValue getValue() const { return Value.getValue(); }
ValueOwnershipKind getOwnershipKind() const {
return Value.getOwnershipKind();
}
/// Return a managed value appropriate for the final use of this CMV.
ManagedValue getFinalManagedValue() const { return Value; }
/// Get the value as an unmanaged ManagedValue.
///
/// You probably should not be using this; it's here to make it easy
/// to find code that is probably wrong.
ManagedValue asUnmanagedValue() const {
return ManagedValue::forUnmanaged(Value.getValue());
}
/// Return the consumption rules appropriate for the final use of
/// this CMV.
CastConsumptionKind getFinalConsumption() const { return FinalConsumption; }
/// Return a managed value that's appropriate for borrowing this
/// value and promising not to consume it.
ConsumableManagedValue asBorrowedOperand() const {
return { asUnmanagedValue(), CastConsumptionKind::CopyOnSuccess };
}
/// Return a managed value that's appropriate for copying this value and
/// always consuming it.
ConsumableManagedValue copy(SILGenFunction &SGF, SILLocation loc) const {
return ConsumableManagedValue::forOwned(asUnmanagedValue().copy(SGF, loc));
}
};
} // namespace Lowering
} // namespace swift
namespace swift {
template <typename To> inline bool isa(const Lowering::ManagedValue &M) {
return isa<To>(M.getValue());
}
} // end namespace swift
#endif