blob: b2455bfa721fb85861185ac035744240940ec252 [file] [log] [blame]
//===--- InstructionUtils.cpp - Utilities for SIL instructions ------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sil-inst-utils"
#include "swift/SIL/InstructionUtils.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/Basic/NullablePtr.h"
#include "swift/SIL/Projection.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILVisitor.h"
using namespace swift;
/// Strip off casts/indexing insts/address projections from V until there is
/// nothing left to strip.
/// FIXME: Why don't we strip projections after stripping indexes?
SILValue swift::getUnderlyingObject(SILValue V) {
while (true) {
SILValue V2 = stripIndexingInsts(stripAddressProjections(stripCasts(V)));
if (V2 == V)
return V2;
V = V2;
}
}
SILValue swift::getUnderlyingAddressRoot(SILValue V) {
while (true) {
SILValue V2 = stripIndexingInsts(stripCasts(V));
switch (V2->getKind()) {
case ValueKind::StructElementAddrInst:
case ValueKind::TupleElementAddrInst:
case ValueKind::UncheckedTakeEnumDataAddrInst:
V2 = cast<SingleValueInstruction>(V2)->getOperand(0);
break;
default:
break;
}
if (V2 == V)
return V2;
V = V2;
}
}
SILValue swift::getUnderlyingObjectStopAtMarkDependence(SILValue V) {
while (true) {
SILValue V2 = stripIndexingInsts(stripAddressProjections(stripCastsWithoutMarkDependence(V)));
if (V2 == V)
return V2;
V = V2;
}
}
static bool isRCIdentityPreservingCast(ValueKind Kind) {
switch (Kind) {
case ValueKind::UpcastInst:
case ValueKind::UncheckedRefCastInst:
case ValueKind::UnconditionalCheckedCastInst:
case ValueKind::UnconditionalCheckedCastValueInst:
case ValueKind::RefToBridgeObjectInst:
case ValueKind::BridgeObjectToRefInst:
return true;
default:
return false;
}
}
/// Return the underlying SILValue after stripping off identity SILArguments if
/// we belong to a BB with one predecessor.
SILValue swift::stripSinglePredecessorArgs(SILValue V) {
while (true) {
auto *A = dyn_cast<SILArgument>(V);
if (!A)
return V;
SILBasicBlock *BB = A->getParent();
// First try and grab the single predecessor of our parent BB. If we don't
// have one, bail.
SILBasicBlock *Pred = BB->getSinglePredecessorBlock();
if (!Pred)
return V;
// Then grab the terminator of Pred...
TermInst *PredTI = Pred->getTerminator();
// And attempt to find our matching argument.
//
// *NOTE* We can only strip things here if we know that there is no semantic
// change in terms of upcasts/downcasts/enum extraction since this is used
// by other routines here. This means that we can only look through
// cond_br/br.
//
// For instance, routines that use stripUpcasts() do not want to strip off a
// downcast that results from checked_cast_br.
if (auto *BI = dyn_cast<BranchInst>(PredTI)) {
V = BI->getArg(A->getIndex());
continue;
}
if (auto *CBI = dyn_cast<CondBranchInst>(PredTI)) {
if (SILValue Arg = CBI->getArgForDestBB(BB, A)) {
V = Arg;
continue;
}
}
return V;
}
}
SILValue swift::stripCastsWithoutMarkDependence(SILValue V) {
while (true) {
V = stripSinglePredecessorArgs(V);
auto K = V->getKind();
if (isRCIdentityPreservingCast(K) ||
K == ValueKind::UncheckedTrivialBitCastInst) {
V = cast<SingleValueInstruction>(V)->getOperand(0);
continue;
}
return V;
}
}
SILValue swift::stripCasts(SILValue V) {
while (true) {
V = stripSinglePredecessorArgs(V);
auto K = V->getKind();
if (isRCIdentityPreservingCast(K)
|| K == ValueKind::UncheckedTrivialBitCastInst
|| K == ValueKind::MarkDependenceInst) {
V = cast<SingleValueInstruction>(V)->getOperand(0);
continue;
}
return V;
}
}
SILValue swift::stripUpCasts(SILValue V) {
assert(V->getType().isClassOrClassMetatype() &&
"Expected class or class metatype!");
V = stripSinglePredecessorArgs(V);
while (auto upcast = dyn_cast<UpcastInst>(V))
V = stripSinglePredecessorArgs(upcast->getOperand());
return V;
}
SILValue swift::stripClassCasts(SILValue V) {
while (true) {
if (auto *UI = dyn_cast<UpcastInst>(V)) {
V = UI->getOperand();
continue;
}
if (auto *UCCI = dyn_cast<UnconditionalCheckedCastInst>(V)) {
V = UCCI->getOperand();
continue;
}
return V;
}
}
SILValue swift::stripAddressProjections(SILValue V) {
while (true) {
V = stripSinglePredecessorArgs(V);
if (!Projection::isAddressProjection(V))
return V;
V = cast<SingleValueInstruction>(V)->getOperand(0);
}
}
SILValue swift::stripUnaryAddressProjections(SILValue V) {
while (true) {
V = stripSinglePredecessorArgs(V);
if (!Projection::isAddressProjection(V))
return V;
auto *Inst = cast<SingleValueInstruction>(V);
if (Inst->getNumOperands() > 1)
return V;
V = Inst->getOperand(0);
}
}
SILValue swift::stripValueProjections(SILValue V) {
while (true) {
V = stripSinglePredecessorArgs(V);
if (!Projection::isObjectProjection(V))
return V;
V = cast<SingleValueInstruction>(V)->getOperand(0);
}
}
SILValue swift::stripIndexingInsts(SILValue V) {
while (true) {
if (!isa<IndexingInst>(V))
return V;
V = cast<IndexingInst>(V)->getBase();
}
}
SILValue swift::stripExpectIntrinsic(SILValue V) {
auto *BI = dyn_cast<BuiltinInst>(V);
if (!BI)
return V;
if (BI->getIntrinsicInfo().ID != llvm::Intrinsic::expect)
return V;
return BI->getArguments()[0];
}
SILValue swift::stripBorrow(SILValue V) {
if (auto *BBI = dyn_cast<BeginBorrowInst>(V))
return BBI->getOperand();
return V;
}
namespace {
enum class OwnershipQualifiedKind {
NotApplicable,
Qualified,
Unqualified,
};
struct OwnershipQualifiedKindVisitor : SILInstructionVisitor<OwnershipQualifiedKindVisitor, OwnershipQualifiedKind> {
OwnershipQualifiedKind visitSILInstruction(SILInstruction *I) {
return OwnershipQualifiedKind::NotApplicable;
}
#define QUALIFIED_INST(CLASS) \
OwnershipQualifiedKind visit ## CLASS(CLASS *I) { \
return OwnershipQualifiedKind::Qualified; \
}
QUALIFIED_INST(EndBorrowInst)
QUALIFIED_INST(LoadBorrowInst)
QUALIFIED_INST(CopyValueInst)
QUALIFIED_INST(CopyUnownedValueInst)
QUALIFIED_INST(DestroyValueInst)
#undef QUALIFIED_INST
OwnershipQualifiedKind visitLoadInst(LoadInst *LI) {
if (LI->getOwnershipQualifier() == LoadOwnershipQualifier::Unqualified)
return OwnershipQualifiedKind::Unqualified;
return OwnershipQualifiedKind::Qualified;
}
OwnershipQualifiedKind visitStoreInst(StoreInst *SI) {
if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Unqualified)
return OwnershipQualifiedKind::Unqualified;
return OwnershipQualifiedKind::Qualified;
}
};
} // end anonymous namespace
bool FunctionOwnershipEvaluator::evaluate(SILInstruction *I) {
assert(I->getFunction() == F.get() && "Can not evaluate function ownership "
"implications of an instruction that "
"does not belong to the instruction "
"that we are evaluating");
switch (OwnershipQualifiedKindVisitor().visit(I)) {
case OwnershipQualifiedKind::Unqualified: {
// If we already know that the function has unqualified ownership, just
// return early.
if (!F.get()->hasQualifiedOwnership())
return true;
// Ok, so we know at this point that we have qualified ownership. If we have
// seen any instructions with qualified ownership, we have an error since
// the function mixes qualified and unqualified instructions.
if (HasOwnershipQualifiedInstruction)
return false;
// Otherwise, set the function to have unqualified ownership. This will
// ensure that no more Qualified instructions can be added to the given
// function.
F.get()->setUnqualifiedOwnership();
return true;
}
case OwnershipQualifiedKind::Qualified: {
// First check if our function has unqualified ownership. If we already do
// have unqualified ownership, then we know that we have already seen an
// unqualified ownership instruction. This means the function has both
// qualified and unqualified instructions. =><=.
if (!F.get()->hasQualifiedOwnership())
return false;
// Ok, at this point we know that we are still qualified. Since functions
// start as qualified, we need to set the HasOwnershipQualifiedInstructions
// so we do not need to look back through the function if we see an
// unqualified instruction later on.
HasOwnershipQualifiedInstruction = true;
return true;
}
case OwnershipQualifiedKind::NotApplicable: {
// Not Applicable instr
return true;
}
}
llvm_unreachable("Unhandled OwnershipQualifiedKind in switch.");
}