blob: e2db1f8c4bf32c1743d0b678e7e645880a0ea562 [file] [log] [blame]
//===--- MemoryBehavior.cpp -----------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sil-membehavior"
#include "swift/SILAnalysis/AliasAnalysis.h"
#include "swift/SILAnalysis/SideEffectAnalysis.h"
#include "swift/SILAnalysis/ValueTracking.h"
#include "swift/SIL/SILVisitor.h"
#include "llvm/Support/Debug.h"
using namespace swift;
//===----------------------------------------------------------------------===//
// TBAA Utilities
//===----------------------------------------------------------------------===//
/// Is this an instruction that can act as a type "oracle" allowing typed access
/// TBAA to know what the real types associated with the SILInstruction are.
static bool isTypedAccessOracle(SILInstruction *I) {
switch (I->getKind()) {
case ValueKind::RefElementAddrInst:
case ValueKind::StructElementAddrInst:
case ValueKind::TupleElementAddrInst:
case ValueKind::UncheckedTakeEnumDataAddrInst:
case ValueKind::LoadInst:
case ValueKind::StoreInst:
case ValueKind::AllocStackInst:
case ValueKind::AllocBoxInst:
case ValueKind::DeallocStackInst:
case ValueKind::DeallocBoxInst:
return true;
default:
return false;
}
}
/// Look at the origin/user ValueBase of V to see if any of them are
/// TypedAccessOracle which enable one to ascertain via undefined behavior the
/// "true" type of the instruction.
SILType swift::findTypedAccessType(SILValue V) {
// First look at the origin of V and see if we have any instruction that is a
// typed oracle.
if (auto *I = dyn_cast<SILInstruction>(V))
if (isTypedAccessOracle(I))
return V.getType();
// Then look at any uses of V that potentially could act as a typed access
// oracle.
for (auto Use : V.getUses())
if (isTypedAccessOracle(Use->getUser()))
return V.getType();
// Otherwise return an empty SILType
return SILType();
}
//===----------------------------------------------------------------------===//
// Memory Behavior Implementation
//===----------------------------------------------------------------------===//
namespace {
using MemBehavior = SILInstruction::MemoryBehavior;
/// Visitor that determines the memory behavior of an instruction relative to a
/// specific SILValue (i.e. can the instruction cause the value to be read,
/// etc.).
class MemoryBehaviorVisitor
: public SILInstructionVisitor<MemoryBehaviorVisitor, MemBehavior> {
/// The alias analysis for any queries we may need.
AliasAnalysis &AA;
/// The value we are attempting to discover memory behavior relative to.
SILValue V;
/// The SILType of the value.
Optional<SILType> TypedAccessTy;
/// Should we treat instructions that increment ref counts as None instead of
/// MayHaveSideEffects.
bool IgnoreRefCountIncrements;
public:
MemoryBehaviorVisitor(AliasAnalysis &AA, SILValue V, bool IgnoreRefCountIncs)
: AA(AA), V(V), IgnoreRefCountIncrements(IgnoreRefCountIncs) {}
SILType getTypedAccessType() {
if (!TypedAccessTy)
TypedAccessTy = findTypedAccessType(V);
return *TypedAccessTy;
}
MemBehavior visitValueBase(ValueBase *V) {
llvm_unreachable("unimplemented");
}
MemBehavior visitSILInstruction(SILInstruction *Inst) {
// If we do not have any more information, just use the general memory
// behavior implementation.
auto Behavior = Inst->getMemoryBehavior();
if (!isLetPointer(V))
return Behavior;
switch (Behavior) {
case MemBehavior::MayHaveSideEffects:
return MemBehavior::MayRead;
case MemBehavior::MayReadWrite:
return MemBehavior::MayRead;
case MemBehavior::MayWrite:
return MemBehavior::None;
default:
return Behavior;
}
}
MemBehavior visitLoadInst(LoadInst *LI);
MemBehavior visitStoreInst(StoreInst *SI);
MemBehavior visitApplyInst(ApplyInst *AI);
MemBehavior visitTryApplyInst(TryApplyInst *AI);
MemBehavior visitBuiltinInst(BuiltinInst *BI);
MemBehavior visitStrongReleaseInst(StrongReleaseInst *BI);
MemBehavior visitUnownedReleaseInst(UnownedReleaseInst *BI);
MemBehavior visitReleaseValueInst(ReleaseValueInst *BI);
// Instructions which are none if our SILValue does not alias one of its
// arguments. If we can not prove such a thing, return the relevant memory
// behavior.
#define OPERANDALIAS_MEMBEHAVIOR_INST(Name) \
MemBehavior visit##Name(Name *I) { \
for (Operand &Op : I->getAllOperands()) { \
if (!AA.isNoAlias(Op.get(), V)) { \
DEBUG(llvm::dbgs() << " " #Name \
" does alias inst. Returning Normal behavior.\n"); \
return I->getMemoryBehavior(); \
} \
} \
\
DEBUG(llvm::dbgs() << " " #Name " does not alias inst. Returning " \
"None.\n"); \
return MemBehavior::None; \
}
OPERANDALIAS_MEMBEHAVIOR_INST(InjectEnumAddrInst)
OPERANDALIAS_MEMBEHAVIOR_INST(UncheckedTakeEnumDataAddrInst)
OPERANDALIAS_MEMBEHAVIOR_INST(InitExistentialAddrInst)
OPERANDALIAS_MEMBEHAVIOR_INST(DeinitExistentialAddrInst)
OPERANDALIAS_MEMBEHAVIOR_INST(DeallocStackInst)
OPERANDALIAS_MEMBEHAVIOR_INST(FixLifetimeInst)
#undef OPERANDALIAS_MEMBEHAVIOR_INST
// Override simple behaviors where MayHaveSideEffects is too general and
// encompasses other behavior that is not read/write/ref count decrement
// behavior we care about.
#define SIMPLE_MEMBEHAVIOR_INST(Name, Behavior) \
MemBehavior visit##Name(Name *I) { return MemBehavior::Behavior; }
SIMPLE_MEMBEHAVIOR_INST(CondFailInst, None)
#undef SIMPLE_MEMBEHAVIOR_INST
// If we are asked to treat ref count increments as being inert, return None
// for these.
//
// FIXME: Once we separate the notion of ref counts from reading/writing
// memory this will be unnecessary.
#define REFCOUNTINC_MEMBEHAVIOR_INST(Name) \
MemBehavior visit##Name(Name *I) { \
if (IgnoreRefCountIncrements) \
return MemBehavior::None; \
return I->getMemoryBehavior(); \
}
REFCOUNTINC_MEMBEHAVIOR_INST(StrongRetainInst)
REFCOUNTINC_MEMBEHAVIOR_INST(StrongRetainAutoreleasedInst)
REFCOUNTINC_MEMBEHAVIOR_INST(StrongRetainUnownedInst)
REFCOUNTINC_MEMBEHAVIOR_INST(UnownedRetainInst)
REFCOUNTINC_MEMBEHAVIOR_INST(RetainValueInst)
#undef REFCOUNTINC_MEMBEHAVIOR_INST
};
} // end anonymous namespace
MemBehavior MemoryBehaviorVisitor::visitLoadInst(LoadInst *LI) {
if (AA.isNoAlias(LI->getOperand(), V, LI->getOperand().getType(),
getTypedAccessType())) {
DEBUG(llvm::dbgs() << " Load Operand does not alias inst. Returning "
"None.\n");
return MemBehavior::None;
}
DEBUG(llvm::dbgs() << " Could not prove that load inst does not alias "
"pointer. Returning may read.");
return MemBehavior::MayRead;
}
MemBehavior MemoryBehaviorVisitor::visitStoreInst(StoreInst *SI) {
// No store besides the initialization of a "let"-variable
// can have any effect on the value of this "let" variable.
if (isLetPointer(V) && SI->getDest() != V)
return MemBehavior::None;
// If the store dest cannot alias the pointer in question, then the
// specified value can not be modified by the store.
if (AA.isNoAlias(SI->getDest(), V, SI->getDest().getType(),
getTypedAccessType())) {
DEBUG(llvm::dbgs() << " Store Dst does not alias inst. Returning "
"None.\n");
return MemBehavior::None;
}
// Otherwise, a store just writes.
DEBUG(llvm::dbgs() << " Could not prove store does not alias inst. "
"Returning MayWrite.\n");
return MemBehavior::MayWrite;
}
MemBehavior MemoryBehaviorVisitor::visitBuiltinInst(BuiltinInst *BI) {
// If our callee is not a builtin, be conservative and return may have side
// effects.
if (!BI) {
return MemBehavior::MayHaveSideEffects;
}
// If the builtin is read none, it does not read or write memory.
if (!BI->mayReadOrWriteMemory()) {
DEBUG(llvm::dbgs() << " Found apply of read none builtin. Returning"
" None.\n");
return MemBehavior::None;
}
// If the builtin is side effect free, then it can only read memory.
if (!BI->mayHaveSideEffects()) {
DEBUG(llvm::dbgs() << " Found apply of side effect free builtin. "
"Returning MayRead.\n");
return MemBehavior::MayRead;
}
// FIXME: If the value (or any other values from the instruction that the
// value comes from) that we are tracking does not escape and we don't alias
// any of the arguments of the apply inst, we should be ok.
// Otherwise be conservative and return that we may have side effects.
DEBUG(llvm::dbgs() << " Found apply of side effect builtin. "
"Returning MayHaveSideEffects.\n");
return MemBehavior::MayHaveSideEffects;
}
MemBehavior MemoryBehaviorVisitor::visitTryApplyInst(TryApplyInst *AI) {
MemBehavior Behavior = MemBehavior::MayHaveSideEffects;
// If it is an allocstack which does not escape, tryapply instruction can not
// read/modify the memory.
if (auto *ASI = dyn_cast<AllocStackInst>(getUnderlyingObject(V)))
if (isNonEscapingLocalObject(ASI->getAddressResult()))
Behavior = MemBehavior::None;
// Otherwise be conservative and return that we may have side effects.
DEBUG(llvm::dbgs() << " Found tryapply, returning " << Behavior << '\n');
return Behavior;
}
MemBehavior MemoryBehaviorVisitor::visitApplyInst(ApplyInst *AI) {
SideEffectAnalysis::FunctionEffects ApplyEffects;
AA.getSideEffectAnalysis()->getEffects(ApplyEffects, AI);
MemBehavior Behavior = MemBehavior::None;
// We can ignore mayTrap().
if (ApplyEffects.mayReadRC() ||
(!IgnoreRefCountIncrements && ApplyEffects.mayAllocObjects())) {
Behavior = MemBehavior::MayHaveSideEffects;
} else {
auto &GlobalEffects = ApplyEffects.getGlobalEffects();
Behavior = GlobalEffects.getMemBehavior(IgnoreRefCountIncrements);
// Check all parameter effects.
for (unsigned Idx = 0, End = AI->getNumArguments();
Idx < End && Behavior < MemBehavior::MayHaveSideEffects; ++Idx) {
auto &ArgEffect = ApplyEffects.getParameterEffects()[Idx];
auto ArgBehavior = ArgEffect.getMemBehavior(IgnoreRefCountIncrements);
if (ArgBehavior > Behavior) {
SILValue Arg = AI->getArgument(Idx);
// We only consider the argument effects if the argument aliases V.
if (!Arg.getType().isAddress() ||
!AA.isNoAlias(Arg, V, Arg.getType(), getTypedAccessType())) {
Behavior = ArgBehavior;
}
}
}
}
if (Behavior > MemBehavior::MayRead && isLetPointer(V))
Behavior = MemBehavior::MayRead;
// If it is an allocstack which does not escape, apply instruction can not
// read/modify the memory.
if (auto *ASI = dyn_cast<AllocStackInst>(getUnderlyingObject(V)))
if (isNonEscapingLocalObject(ASI->getAddressResult())) {
Behavior = MemBehavior::None;
}
DEBUG(llvm::dbgs() << " Found apply, returning " << Behavior << '\n');
return Behavior;
}
MemBehavior
MemoryBehaviorVisitor::visitStrongReleaseInst(StrongReleaseInst *SI) {
// Need to make sure that the allocated memory does not escape.
// AllocBox to stack does not check for whether the address of promoted
// allocstack can escape.
//
// TODO: come up with a test case which shows isNonEscapingLocalObject is
// necessary.
if (AllocStackInst *ASI = dyn_cast<AllocStackInst>(getUnderlyingObject(V)))
if (isNonEscapingLocalObject(ASI->getAddressResult()))
return MemBehavior::None;
return MemBehavior::MayHaveSideEffects;
}
MemBehavior
MemoryBehaviorVisitor::visitUnownedReleaseInst(UnownedReleaseInst *SI) {
// Need to make sure that the allocated memory does not escape.
if (AllocStackInst *ASI = dyn_cast<AllocStackInst>(getUnderlyingObject(V)))
if (isNonEscapingLocalObject(ASI->getAddressResult()))
return MemBehavior::None;
return MemBehavior::MayHaveSideEffects;
}
MemBehavior MemoryBehaviorVisitor::visitReleaseValueInst(ReleaseValueInst *SI) {
// Need to make sure that the allocated memory does not escape.
if (AllocStackInst *ASI = dyn_cast<AllocStackInst>(getUnderlyingObject(V)))
if (isNonEscapingLocalObject(ASI->getAddressResult()))
return MemBehavior::None;
return MemBehavior::MayHaveSideEffects;
}
//===----------------------------------------------------------------------===//
// Top Level Entrypoint
//===----------------------------------------------------------------------===//
SILInstruction::MemoryBehavior
AliasAnalysis::getMemoryBehavior(SILInstruction *Inst, SILValue V,
bool IgnoreRefCountIncrements) {
DEBUG(llvm::dbgs() << "GET MEMORY BEHAVIOR FOR:\n " << *Inst << " "
<< *V.getDef());
return MemoryBehaviorVisitor(*this, V, IgnoreRefCountIncrements).visit(Inst);
}