mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
348 lines
14 KiB
C++
348 lines
14 KiB
C++
//===--- MemoryBehavior.cpp -----------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 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/SILOptimizer/Analysis/AliasAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/EscapeAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/ValueTracking.h"
|
|
#include "swift/SIL/SILVisitor.h"
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
using namespace swift;
|
|
|
|
// The MemoryBehavior Cache must not grow beyond this size.
|
|
// We limit the size of the MB cache to 2**14 because we want to limit the
|
|
// memory usage of this cache.
|
|
static const int MemoryBehaviorAnalysisMaxCacheSize = 16384;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 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> {
|
|
|
|
AliasAnalysis *AA;
|
|
|
|
SideEffectAnalysis *SEA;
|
|
|
|
EscapeAnalysis *EA;
|
|
|
|
/// 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.
|
|
RetainObserveKind InspectionMode;
|
|
|
|
public:
|
|
MemoryBehaviorVisitor(AliasAnalysis *AA, SideEffectAnalysis *SEA,
|
|
EscapeAnalysis *EA, SILValue V,
|
|
RetainObserveKind IgnoreRefCountIncs)
|
|
: AA(AA), SEA(SEA), EA(EA), V(V), InspectionMode(IgnoreRefCountIncs) {}
|
|
|
|
SILType getValueTBAAType() {
|
|
if (!TypedAccessTy)
|
|
TypedAccessTy = computeTBAAType(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();
|
|
bool ReadOnlyAccess = isLetPointer(V);
|
|
|
|
// If this is a regular read-write access then return the computed memory
|
|
// behavior.
|
|
if (!ReadOnlyAccess)
|
|
return Behavior;
|
|
|
|
// If this is a read-only access to 'let variable' then we can strip away
|
|
// the write access.
|
|
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 cannot 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 (InspectionMode == RetainObserveKind::IgnoreRetains) \
|
|
return MemBehavior::None; \
|
|
return I->getMemoryBehavior(); \
|
|
}
|
|
REFCOUNTINC_MEMBEHAVIOR_INST(StrongRetainInst)
|
|
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, computeTBAAType(LI->getOperand()),
|
|
getValueTBAAType())) {
|
|
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 cannot be modified by the store.
|
|
if (AA->isNoAlias(SI->getDest(), V, computeTBAAType(SI->getDest()),
|
|
getValueTBAAType())) {
|
|
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;
|
|
// Ask escape analysis.
|
|
if (!EA->canObjectOrContentEscapeTo(V, AI))
|
|
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;
|
|
SEA->getEffects(ApplyEffects, AI);
|
|
|
|
MemBehavior Behavior = MemBehavior::None;
|
|
|
|
// We can ignore mayTrap().
|
|
if (ApplyEffects.mayReadRC() ||
|
|
(InspectionMode == RetainObserveKind::ObserveRetains &&
|
|
ApplyEffects.mayAllocObjects())) {
|
|
Behavior = MemBehavior::MayHaveSideEffects;
|
|
} else {
|
|
auto &GlobalEffects = ApplyEffects.getGlobalEffects();
|
|
Behavior = GlobalEffects.getMemBehavior(InspectionMode);
|
|
|
|
// 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(InspectionMode);
|
|
auto NewBehavior = combineMemoryBehavior(Behavior, ArgBehavior);
|
|
if (NewBehavior != 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, computeTBAAType(Arg), getValueTBAAType())) {
|
|
Behavior = NewBehavior;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (Behavior > MemBehavior::None) {
|
|
if (Behavior > MemBehavior::MayRead && isLetPointer(V))
|
|
Behavior = MemBehavior::MayRead;
|
|
|
|
// Ask escape analysis.
|
|
if (!EA->canObjectOrContentEscapeTo(V, AI))
|
|
Behavior = MemBehavior::None;
|
|
}
|
|
DEBUG(llvm::dbgs() << " Found apply, returning " << Behavior << '\n');
|
|
return Behavior;
|
|
}
|
|
|
|
MemBehavior
|
|
MemoryBehaviorVisitor::visitStrongReleaseInst(StrongReleaseInst *SI) {
|
|
if (!EA->canEscapeTo(V, SI))
|
|
return MemBehavior::None;
|
|
return MemBehavior::MayHaveSideEffects;
|
|
}
|
|
|
|
MemBehavior
|
|
MemoryBehaviorVisitor::visitUnownedReleaseInst(UnownedReleaseInst *SI) {
|
|
if (!EA->canEscapeTo(V, SI))
|
|
return MemBehavior::None;
|
|
return MemBehavior::MayHaveSideEffects;
|
|
}
|
|
|
|
MemBehavior MemoryBehaviorVisitor::visitReleaseValueInst(ReleaseValueInst *SI) {
|
|
if (!EA->canEscapeTo(V, SI))
|
|
return MemBehavior::None;
|
|
return MemBehavior::MayHaveSideEffects;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Top Level Entrypoint
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
MemBehavior
|
|
AliasAnalysis::computeMemoryBehavior(SILInstruction *Inst, SILValue V,
|
|
RetainObserveKind InspectionMode) {
|
|
MemBehaviorKeyTy Key = toMemoryBehaviorKey(SILValue(Inst), V,
|
|
InspectionMode);
|
|
// Check if we've already computed this result.
|
|
auto It = MemoryBehaviorCache.find(Key);
|
|
if (It != MemoryBehaviorCache.end()) {
|
|
return It->second;
|
|
}
|
|
|
|
// Flush the cache if the size of the cache is too large.
|
|
if (MemoryBehaviorCache.size() > MemoryBehaviorAnalysisMaxCacheSize) {
|
|
MemoryBehaviorCache.clear();
|
|
MemoryBehaviorValueBaseToIndex.clear();
|
|
|
|
// Key is no longer valid as we cleared the MemoryBehaviorValueBaseToIndex.
|
|
Key = toMemoryBehaviorKey(SILValue(Inst), V, InspectionMode);
|
|
}
|
|
|
|
// Calculate the aliasing result and store it in the cache.
|
|
auto Result = computeMemoryBehaviorInner(Inst, V, InspectionMode);
|
|
MemoryBehaviorCache[Key] = Result;
|
|
return Result;
|
|
}
|
|
|
|
MemBehavior
|
|
AliasAnalysis::computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V,
|
|
RetainObserveKind InspectionMode) {
|
|
DEBUG(llvm::dbgs() << "GET MEMORY BEHAVIOR FOR:\n " << *Inst << " "
|
|
<< *V);
|
|
assert(SEA && "SideEffectsAnalysis must be initialized!");
|
|
return MemoryBehaviorVisitor(this, SEA, EA, V, InspectionMode).visit(Inst);
|
|
}
|
|
|
|
MemBehaviorKeyTy AliasAnalysis::toMemoryBehaviorKey(SILValue V1, SILValue V2,
|
|
RetainObserveKind M) {
|
|
size_t idx1 = MemoryBehaviorValueBaseToIndex.getIndex(V1);
|
|
assert(idx1 != std::numeric_limits<size_t>::max() &&
|
|
"~0 index reserved for empty/tombstone keys");
|
|
size_t idx2 = MemoryBehaviorValueBaseToIndex.getIndex(V2);
|
|
assert(idx2 != std::numeric_limits<size_t>::max() &&
|
|
"~0 index reserved for empty/tombstone keys");
|
|
return {idx1, idx2, M};
|
|
}
|