mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Now that OperandOwnership determines the operand constraints, it doesn't make sense to distinguish between Borrow and NestedBorrow at this level. We want these uses to automatically convert between the nested/non-nested state as the operand's ownership changes. The use does not need to impose any constraint on the ownership of the incoming value. For algorithms that need to distinguish nested borrows, it's still trivial to do so.
429 lines
15 KiB
C++
429 lines
15 KiB
C++
//===--- SILValue.cpp - Implementation for SILValue -----------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/SIL/SILValue.h"
|
|
#include "swift/SIL/OwnershipUtils.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILBuiltinVisitor.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SIL/SILModule.h"
|
|
#include "swift/SIL/SILVisitor.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
|
|
using namespace swift;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Check SILNode Type Properties
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// These are just for performance and verification. If one needs to make
|
|
/// changes that cause the asserts the fire, please update them. The purpose is
|
|
/// to prevent these predicates from changing values by mistake.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Check SILValue Type Properties
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// These are just for performance and verification. If one needs to make
|
|
/// changes that cause the asserts the fire, please update them. The purpose is
|
|
/// to prevent these predicates from changing values by mistake.
|
|
static_assert(std::is_standard_layout<SILValue>::value,
|
|
"Expected SILValue to be standard layout");
|
|
static_assert(sizeof(SILValue) == sizeof(uintptr_t),
|
|
"SILValue should be pointer sized");
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Utility Methods
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void ValueBase::replaceAllUsesWith(ValueBase *RHS) {
|
|
assert(this != RHS && "Cannot RAUW a value with itself");
|
|
while (!use_empty()) {
|
|
Operand *Op = *use_begin();
|
|
Op->set(RHS);
|
|
}
|
|
}
|
|
|
|
void ValueBase::replaceAllUsesWithUndef() {
|
|
auto *F = getFunction();
|
|
if (!F) {
|
|
llvm_unreachable("replaceAllUsesWithUndef can only be used on ValueBase "
|
|
"that have access to the parent function.");
|
|
}
|
|
while (!use_empty()) {
|
|
Operand *Op = *use_begin();
|
|
Op->set(SILUndef::get(Op->get()->getType(), *F));
|
|
}
|
|
}
|
|
|
|
SILInstruction *ValueBase::getDefiningInstruction() {
|
|
if (auto *inst = dyn_cast<SingleValueInstruction>(this))
|
|
return inst;
|
|
if (auto *result = dyn_cast<MultipleValueInstructionResult>(this))
|
|
return result->getParent();
|
|
return nullptr;
|
|
}
|
|
|
|
SILInstruction *ValueBase::getDefiningInsertionPoint() {
|
|
if (auto *inst = getDefiningInstruction())
|
|
return inst;
|
|
if (auto *arg = dyn_cast<SILArgument>(this))
|
|
return &*arg->getParentBlock()->begin();
|
|
return nullptr;
|
|
}
|
|
|
|
Optional<ValueBase::DefiningInstructionResult>
|
|
ValueBase::getDefiningInstructionResult() {
|
|
if (auto *inst = dyn_cast<SingleValueInstruction>(this))
|
|
return DefiningInstructionResult{inst, 0};
|
|
if (auto *result = dyn_cast<MultipleValueInstructionResult>(this))
|
|
return DefiningInstructionResult{result->getParent(), result->getIndex()};
|
|
return None;
|
|
}
|
|
|
|
SILBasicBlock *SILNode::getParentBlock() const {
|
|
auto *CanonicalNode =
|
|
const_cast<SILNode *>(this)->getRepresentativeSILNodeInObject();
|
|
if (auto *Inst = dyn_cast<SILInstruction>(CanonicalNode))
|
|
return Inst->getParent();
|
|
if (auto *Arg = dyn_cast<SILArgument>(CanonicalNode))
|
|
return Arg->getParent();
|
|
return nullptr;
|
|
}
|
|
|
|
SILFunction *SILNode::getFunction() const {
|
|
auto *CanonicalNode =
|
|
const_cast<SILNode *>(this)->getRepresentativeSILNodeInObject();
|
|
if (auto *Inst = dyn_cast<SILInstruction>(CanonicalNode))
|
|
return Inst->getFunction();
|
|
if (auto *Arg = dyn_cast<SILArgument>(CanonicalNode))
|
|
return Arg->getFunction();
|
|
return nullptr;
|
|
}
|
|
|
|
SILModule *SILNode::getModule() const {
|
|
auto *CanonicalNode =
|
|
const_cast<SILNode *>(this)->getRepresentativeSILNodeInObject();
|
|
if (auto *Inst = dyn_cast<SILInstruction>(CanonicalNode))
|
|
return &Inst->getModule();
|
|
if (auto *Arg = dyn_cast<SILArgument>(CanonicalNode))
|
|
return &Arg->getModule();
|
|
return nullptr;
|
|
}
|
|
|
|
const SILNode *SILNode::getRepresentativeSILNodeSlowPath() const {
|
|
assert(getStorageLoc() != SILNodeStorageLocation::Instruction);
|
|
|
|
if (isa<SingleValueInstruction>(this)) {
|
|
assert(hasMultipleSILNodeBases(getKind()));
|
|
return &static_cast<const SILInstruction &>(
|
|
static_cast<const SingleValueInstruction &>(
|
|
static_cast<const ValueBase &>(*this)));
|
|
}
|
|
|
|
if (auto *MVR = dyn_cast<MultipleValueInstructionResult>(this)) {
|
|
return MVR->getParent();
|
|
}
|
|
|
|
llvm_unreachable("Invalid value for slow path");
|
|
}
|
|
|
|
/// Get a location for this value.
|
|
SILLocation SILValue::getLoc() const {
|
|
if (auto *instr = Value->getDefiningInstruction())
|
|
return instr->getLoc();
|
|
|
|
if (auto *arg = dyn_cast<SILArgument>(*this)) {
|
|
if (arg->getDecl())
|
|
return RegularLocation(const_cast<ValueDecl *>(arg->getDecl()));
|
|
}
|
|
// TODO: bbargs should probably use one of their operand locations.
|
|
return Value->getFunction()->getLocation();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// OwnershipKind
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
|
|
const OwnershipKind &kind) {
|
|
return os << kind.asString();
|
|
}
|
|
|
|
StringRef OwnershipKind::asString() const {
|
|
switch (value) {
|
|
case OwnershipKind::Any:
|
|
return "any";
|
|
case OwnershipKind::Unowned:
|
|
return "unowned";
|
|
case OwnershipKind::Owned:
|
|
return "owned";
|
|
case OwnershipKind::Guaranteed:
|
|
return "guaranteed";
|
|
case OwnershipKind::None:
|
|
return "none";
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ValueOwnershipKind
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
ValueOwnershipKind::ValueOwnershipKind(const SILFunction &F, SILType Type,
|
|
SILArgumentConvention Convention)
|
|
: value(OwnershipKind::Any) {
|
|
auto &M = F.getModule();
|
|
|
|
// Trivial types can be passed using a variety of conventions. They always
|
|
// have trivial ownership.
|
|
if (Type.isTrivial(F)) {
|
|
value = OwnershipKind::None;
|
|
return;
|
|
}
|
|
|
|
switch (Convention) {
|
|
case SILArgumentConvention::Indirect_In:
|
|
case SILArgumentConvention::Indirect_In_Constant:
|
|
value = SILModuleConventions(M).useLoweredAddresses()
|
|
? OwnershipKind::None
|
|
: OwnershipKind::Owned;
|
|
break;
|
|
case SILArgumentConvention::Indirect_In_Guaranteed:
|
|
value = SILModuleConventions(M).useLoweredAddresses()
|
|
? OwnershipKind::None
|
|
: OwnershipKind::Guaranteed;
|
|
break;
|
|
case SILArgumentConvention::Indirect_Inout:
|
|
case SILArgumentConvention::Indirect_InoutAliasable:
|
|
case SILArgumentConvention::Indirect_Out:
|
|
value = OwnershipKind::None;
|
|
return;
|
|
case SILArgumentConvention::Direct_Owned:
|
|
value = OwnershipKind::Owned;
|
|
return;
|
|
case SILArgumentConvention::Direct_Unowned:
|
|
value = OwnershipKind::Unowned;
|
|
return;
|
|
case SILArgumentConvention::Direct_Guaranteed:
|
|
value = OwnershipKind::Guaranteed;
|
|
return;
|
|
}
|
|
}
|
|
|
|
StringRef ValueOwnershipKind::asString() const {
|
|
return value.asString();
|
|
}
|
|
|
|
llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
|
|
ValueOwnershipKind kind) {
|
|
return os << kind.asString();
|
|
}
|
|
|
|
ValueOwnershipKind::ValueOwnershipKind(StringRef S)
|
|
: value(OwnershipKind::Any) {
|
|
auto Result = llvm::StringSwitch<Optional<OwnershipKind::innerty>>(S)
|
|
.Case("unowned", OwnershipKind::Unowned)
|
|
.Case("owned", OwnershipKind::Owned)
|
|
.Case("guaranteed", OwnershipKind::Guaranteed)
|
|
.Case("any", OwnershipKind::None)
|
|
.Default(None);
|
|
if (!Result.hasValue())
|
|
llvm_unreachable("Invalid string representation of ValueOwnershipKind");
|
|
value = Result.getValue();
|
|
}
|
|
|
|
ValueOwnershipKind
|
|
ValueOwnershipKind::getProjectedOwnershipKind(const SILFunction &F,
|
|
SILType Proj) const {
|
|
if (Proj.isTrivial(F))
|
|
return OwnershipKind::None;
|
|
return *this;
|
|
}
|
|
|
|
#if 0
|
|
/// Map a SILValue mnemonic name to its ValueKind.
|
|
ValueKind swift::getSILValueKind(StringRef Name) {
|
|
#define SINGLE_VALUE_INST(Id, TextualName, Parent, MemoryBehavior, \
|
|
ReleasingBehavior) \
|
|
if (Name == #TextualName) \
|
|
return ValueKind::Id;
|
|
|
|
#define VALUE(Id, Parent) \
|
|
if (Name == #Id) \
|
|
return ValueKind::Id;
|
|
|
|
#include "swift/SIL/SILNodes.def"
|
|
|
|
#ifdef NDEBUG
|
|
llvm::errs()
|
|
<< "Unknown SILValue name\n";
|
|
abort();
|
|
#endif
|
|
llvm_unreachable("Unknown SILValue name");
|
|
}
|
|
|
|
/// Map ValueKind to a corresponding mnemonic name.
|
|
StringRef swift::getSILValueName(ValueKind Kind) {
|
|
switch (Kind) {
|
|
#define SINGLE_VALUE_INST(Id, TextualName, Parent, MemoryBehavior, \
|
|
ReleasingBehavior) \
|
|
case ValueKind::Id: \
|
|
return #TextualName;
|
|
|
|
#define VALUE(Id, Parent) \
|
|
case ValueKind::Id: \
|
|
return #Id;
|
|
|
|
#include "swift/SIL/SILNodes.def"
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// UseLifetimeConstraint
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
|
|
UseLifetimeConstraint constraint) {
|
|
switch (constraint) {
|
|
case UseLifetimeConstraint::NonLifetimeEnding:
|
|
os << "NonLifetimeEnding";
|
|
break;
|
|
case UseLifetimeConstraint::LifetimeEnding:
|
|
os << "LifetimeEnding";
|
|
break;
|
|
}
|
|
return os;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Operand
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
SILBasicBlock *Operand::getParentBlock() const {
|
|
auto *self = const_cast<Operand *>(this);
|
|
return self->getUser()->getParent();
|
|
}
|
|
|
|
SILFunction *Operand::getParentFunction() const {
|
|
auto *self = const_cast<Operand *>(this);
|
|
return self->getUser()->getFunction();
|
|
}
|
|
|
|
/// Return true if this use can accept Unowned values.
|
|
static bool canAcceptUnownedValue(OperandOwnership operandOwnership) {
|
|
switch (operandOwnership) {
|
|
case OperandOwnership::NonUse:
|
|
case OperandOwnership::UnownedInstantaneousUse:
|
|
case OperandOwnership::ForwardingUnowned:
|
|
case OperandOwnership::PointerEscape:
|
|
case OperandOwnership::BitwiseEscape:
|
|
return true;
|
|
case OperandOwnership::TrivialUse:
|
|
case OperandOwnership::InstantaneousUse:
|
|
case OperandOwnership::Borrow:
|
|
case OperandOwnership::DestroyingConsume:
|
|
case OperandOwnership::ForwardingConsume:
|
|
case OperandOwnership::InteriorPointer:
|
|
case OperandOwnership::ForwardingBorrow:
|
|
case OperandOwnership::EndBorrow:
|
|
case OperandOwnership::Reborrow:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool Operand::canAcceptKind(ValueOwnershipKind kind) const {
|
|
auto operandOwnership = getOperandOwnership();
|
|
auto constraint = operandOwnership.getOwnershipConstraint();
|
|
if (constraint.satisfiesConstraint(kind)) {
|
|
// Constraints aren't precise enough to enforce Unowned value uses.
|
|
if (kind == OwnershipKind::Unowned) {
|
|
return canAcceptUnownedValue(operandOwnership);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Operand::satisfiesConstraints() const {
|
|
return canAcceptKind(get().getOwnershipKind());
|
|
}
|
|
|
|
bool Operand::isLifetimeEnding() const {
|
|
auto constraint = getOwnershipConstraint();
|
|
|
|
// If our use lifetime constraint is NonLifetimeEnding, just return false.
|
|
if (!constraint.isLifetimeEnding())
|
|
return false;
|
|
|
|
// Otherwise, we may have a lifetime ending use. We consider two cases here:
|
|
// the case where our value has OwnershipKind::None and one where it has some
|
|
// other OwnershipKind. Note that values with OwnershipKind::None ownership
|
|
// can not have their lifetime ended since they are outside of the ownership
|
|
// system. Given such a case, if we have such a value we return
|
|
// isLifetimeEnding() as false even if the constraint itself has a constraint
|
|
// that says a value is LifetimeEnding. If we have a value that has a
|
|
// non-OwnershipKind::None ownership then we just return true as expected.
|
|
return get().getOwnershipKind() != OwnershipKind::None;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// OperandConstraint
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
|
|
OwnershipConstraint constraint) {
|
|
return os << "<Constraint "
|
|
"Kind:" << constraint.getPreferredKind()
|
|
<< " LifetimeConstraint:" << constraint.getLifetimeConstraint()
|
|
<< ">";
|
|
}
|
|
|
|
StringRef OperandOwnership::asString() const {
|
|
switch (value) {
|
|
case OperandOwnership::NonUse:
|
|
return "non-use";
|
|
case OperandOwnership::TrivialUse:
|
|
return "trivial-use";
|
|
case OperandOwnership::InstantaneousUse:
|
|
return "instantaneous";
|
|
case OperandOwnership::UnownedInstantaneousUse:
|
|
return "unowned-instantaneous";
|
|
case OperandOwnership::ForwardingUnowned:
|
|
return "forwarding-unowned";
|
|
case OperandOwnership::PointerEscape:
|
|
return "pointer-escape";
|
|
case OperandOwnership::BitwiseEscape:
|
|
return "bitwise-escape";
|
|
case OperandOwnership::Borrow:
|
|
return "borrow";
|
|
case OperandOwnership::DestroyingConsume:
|
|
return "destroying-consume";
|
|
case OperandOwnership::ForwardingConsume:
|
|
return "forwarding-consume";
|
|
case OperandOwnership::InteriorPointer:
|
|
return "interior-pointer";
|
|
case OperandOwnership::ForwardingBorrow:
|
|
return "forwarding-borrow";
|
|
case OperandOwnership::EndBorrow:
|
|
return "end-borrow";
|
|
case OperandOwnership::Reborrow:
|
|
return "reborrow";
|
|
}
|
|
}
|
|
|
|
llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
|
|
const OperandOwnership &operandOwnership) {
|
|
return os << operandOwnership.asString();
|
|
}
|