refactoring: Split MemoryLifetime.cpp/h into three separate files

And rename MemoryDataflow -> BitDataflow.

MemoryLifetime contained MemoryLocations, MemoryDataflow and the MemoryLifetimeVerifier.
Three independent things, for which it makes sense to have them in three separated files.

NFC.
This commit is contained in:
Erik Eckstein
2021-03-12 15:26:59 +01:00
parent ad8252654d
commit 1baf009c06
12 changed files with 847 additions and 785 deletions

View File

@@ -0,0 +1,146 @@
//===--- BitDataflow.h ------------------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 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
//
//===----------------------------------------------------------------------===//
///
/// \file Contains the BitDataflow utility for performing bit-wise dataflow
/// analysis on a SILFunction.
///
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SIL_BIT_DATAFLOW_H
#define SWIFT_SIL_BIT_DATAFLOW_H
#include "swift/SIL/BasicBlockData.h"
#include "llvm/ADT/SmallBitVector.h"
namespace swift {
class SILFunction;
/// A utility to calculate forward or backward dataflow of bit sets on a
/// SILFunction.
class BitDataflow {
/// What kind of terminators can be reached from a block.
enum class ExitReachability : uint8_t {
/// Worst case: the block is part of a cycle which neither reaches a
/// function-exit nor an unreachable-instruction.
InInfiniteLoop,
/// An unreachable-instruction can be reached from the block, but not a
/// function-exit (like "return" or "throw").
ReachesUnreachable,
/// A function-exit can be reached from the block.
/// This is the case for most basic blocks.
ReachesExit
};
public:
using Bits = llvm::SmallBitVector;
/// Basic-block specific information used for dataflow analysis.
struct BlockState {
/// The bits valid at the entry (i.e. the first instruction) of the block.
Bits entrySet;
/// The bits valid at the exit (i.e. after the terminator) of the block.
Bits exitSet;
/// Generated bits of the block.
Bits genSet;
/// Killed bits of the block.
Bits killSet;
/// True, if this block is reachable from the entry block, i.e. is not an
/// unreachable block.
///
/// This flag is only computed if entryReachabilityAnalysis is called.
bool reachableFromEntry = false;
/// What kind of terminators can be reached from this block.
///
/// This is only computed if exitReachableAnalysis is called.
ExitReachability exitReachability = ExitReachability::InInfiniteLoop;
BlockState(unsigned numLocations) :
entrySet(numLocations), exitSet(numLocations),
genSet(numLocations), killSet(numLocations) {}
bool exitReachable() const {
return exitReachability == ExitReachability::ReachesExit;
}
bool isInInfiniteLoop() const {
return exitReachability == ExitReachability::InInfiniteLoop;
}
};
private:
BasicBlockData<BlockState> blockStates;
public:
using iterator = BasicBlockData<BlockState>::iterator;
/// Sets up the BlockState datastructures and associates all basic blocks with
/// a state.
BitDataflow(SILFunction *function, unsigned numLocations);
BitDataflow(const BitDataflow &) = delete;
BitDataflow &operator=(const BitDataflow &) = delete;
iterator begin() { return blockStates.begin(); }
iterator end() { return blockStates.end(); }
/// Returns the state of a block.
BlockState &operator[] (SILBasicBlock *block) {
return blockStates[block];
}
/// Calculates the BlockState::reachableFromEntry flags.
void entryReachabilityAnalysis();
/// Calculates the BlockState::exitReachable flags.
void exitReachableAnalysis();
using JoinOperation = std::function<void (Bits &dest, const Bits &src)>;
/// Derives the block exit sets from the entry sets by applying the gen and
/// kill sets.
/// At control flow joins, the \p join operation is applied.
void solveForward(JoinOperation join);
/// Calls solveForward() with a bit-intersection as join operation.
void solveForwardWithIntersect();
/// Calls solveForward() with a bit-union as join operation.
void solveForwardWithUnion();
/// Derives the block entry sets from the exit sets by applying the gen and
/// kill sets.
/// At control flow joins, the \p join operation is applied.
void solveBackward(JoinOperation join);
/// Calls solveBackward() with a bit-intersection as join operation.
void solveBackwardWithIntersect();
/// Calls solveBackward() with a bit-union as join operation.
void solveBackwardWithUnion();
/// Debug dump the BitDataflow state.
void dump() const;
};
} // end swift namespace
#endif

View File

@@ -1,8 +1,8 @@
//===--- MemoryLifetime.h ---------------------------------------*- C++ -*-===// //===--- MemoryLocations.h --------------------------------------*- C++ -*-===//
// //
// This source file is part of the Swift.org open source project // This source file is part of the Swift.org open source project
// //
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception // Licensed under Apache License v2.0 with Runtime Library Exception
// //
// See https://swift.org/LICENSE.txt for license information // See https://swift.org/LICENSE.txt for license information
@@ -10,19 +10,25 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// ///
/// \file Contains utilities for calculating and verifying memory lifetime. /// \file Contains the MemoryLocations utility for analyzing memory locations in
/// a SILFunction.
/// ///
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#ifndef SWIFT_SIL_MEMORY_LIFETIME_H #ifndef SWIFT_SIL_MEMORY_LOCATIONS_H
#define SWIFT_SIL_MEMORY_LIFETIME_H #define SWIFT_SIL_MEMORY_LOCATIONS_H
#include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILValue.h"
#include "swift/SIL/SILFunction.h" #include "llvm/ADT/DenseMap.h"
#include "swift/SIL/BasicBlockData.h" #include "llvm/ADT/SmallBitVector.h"
#include "llvm/Support/raw_ostream.h"
namespace swift { namespace swift {
class SILFunction;
class SILBasicBlock;
class SingleValueInstruction;
void printBitsAsArray(llvm::raw_ostream &OS, const SmallBitVector &bits); void printBitsAsArray(llvm::raw_ostream &OS, const SmallBitVector &bits);
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
@@ -40,10 +46,6 @@ void dumpBits(const SmallBitVector &bits);
/// Currently only a certain set of address instructions are supported: /// Currently only a certain set of address instructions are supported:
/// Specifically those instructions which are going to be included when SIL /// Specifically those instructions which are going to be included when SIL
/// supports opaque values. /// supports opaque values.
/// TODO: Support more address instructions, like cast instructions.
///
/// The MemoryLocations works well together with MemoryDataflow, which can be
/// used to calculate global dataflow of location information.
class MemoryLocations { class MemoryLocations {
public: public:
@@ -115,10 +117,10 @@ public:
/// sub-locations 3 and 4. But bit 0 is set in location 0 (the "self" bit), /// sub-locations 3 and 4. But bit 0 is set in location 0 (the "self" bit),
/// because it represents the untracked field ``Outer.z``. /// because it represents the untracked field ``Outer.z``.
/// ///
/// Single-payload enums are represented by a location with a single sub- /// Enums and existentials are represented by a location with a single sub-
/// location (the projected payload address, i.e. an ``init_enum_data_addr`` /// location (the projected payload/existential address, i.e. an
/// or an ``unchecked_take_enum_data_addr``. /// ``init_enum_data_addr``, ``unchecked_take_enum_data_addr`` or
/// Multi-payload enums are not supported right now. /// ``init_existential_addr``.
Bits subLocations; Bits subLocations;
/// The accumulated parent bits, including the "self" bit. /// The accumulated parent bits, including the "self" bit.
@@ -228,7 +230,7 @@ public:
const Location *getRootLocation(unsigned index) const; const Location *getRootLocation(unsigned index) const;
/// Registers an address projection instruction for a location. /// Registers an address projection instruction for a location.
void registerProjection(SingleValueInstruction *projection, unsigned locIdx) { void registerProjection(SILValue projection, unsigned locIdx) {
addr2LocIdx[projection] = locIdx; addr2LocIdx[projection] = locIdx;
} }
@@ -313,139 +315,6 @@ private:
void initFieldsCounter(Location &loc); void initFieldsCounter(Location &loc);
}; };
/// The MemoryDataflow utility calculates global dataflow of memory locations.
///
/// The MemoryDataflow works well together with MemoryLocations, which can be
/// used to analyze locations as input to the dataflow.
/// TODO: Actuall this utility can be used for any kind of dataflow, not just
/// for memory locations. Consider renaming it.
class MemoryDataflow {
/// What kind of terminators can be reached from a block.
enum class ExitReachability : uint8_t {
/// Worst case: the block is part of a cycle which neither reaches a
/// function-exit nor an unreachable-instruction.
InInfiniteLoop,
/// An unreachable-instruction can be reached from the block, but not a
/// function-exit (like "return" or "throw").
ReachesUnreachable,
/// A function-exit can be reached from the block.
/// This is the case for most basic blocks.
ReachesExit
};
public:
using Bits = MemoryLocations::Bits;
/// Basic-block specific information used for dataflow analysis.
struct BlockState {
/// The bits valid at the entry (i.e. the first instruction) of the block.
Bits entrySet;
/// The bits valid at the exit (i.e. after the terminator) of the block.
Bits exitSet;
/// Generated bits of the block.
Bits genSet;
/// Killed bits of the block.
Bits killSet;
/// True, if this block is reachable from the entry block, i.e. is not an
/// unreachable block.
///
/// This flag is only computed if entryReachabilityAnalysis is called.
bool reachableFromEntry = false;
/// What kind of terminators can be reached from this block.
///
/// This is only computed if exitReachableAnalysis is called.
ExitReachability exitReachability = ExitReachability::InInfiniteLoop;
BlockState(unsigned numLocations) :
entrySet(numLocations), exitSet(numLocations),
genSet(numLocations), killSet(numLocations) {}
// Utility functions for setting and clearing gen- and kill-bits.
void genBits(SILValue addr, const MemoryLocations &locs) {
locs.genBits(genSet, killSet, addr);
}
void killBits(SILValue addr, const MemoryLocations &locs) {
locs.killBits(genSet, killSet, addr);
}
bool exitReachable() const {
return exitReachability == ExitReachability::ReachesExit;
}
bool isInInfiniteLoop() const {
return exitReachability == ExitReachability::InInfiniteLoop;
}
};
private:
BasicBlockData<BlockState> blockStates;
public:
using iterator = BasicBlockData<BlockState>::iterator;
/// Sets up the BlockState datastructures and associates all basic blocks with
/// a state.
MemoryDataflow(SILFunction *function, unsigned numLocations);
MemoryDataflow(const MemoryDataflow &) = delete;
MemoryDataflow &operator=(const MemoryDataflow &) = delete;
iterator begin() { return blockStates.begin(); }
iterator end() { return blockStates.end(); }
/// Returns the state of a block.
BlockState &operator[] (SILBasicBlock *block) {
return blockStates[block];
}
/// Calculates the BlockState::reachableFromEntry flags.
void entryReachabilityAnalysis();
/// Calculates the BlockState::exitReachable flags.
void exitReachableAnalysis();
using JoinOperation = std::function<void (Bits &dest, const Bits &src)>;
/// Derives the block exit sets from the entry sets by applying the gen and
/// kill sets.
/// At control flow joins, the \p join operation is applied.
void solveForward(JoinOperation join);
/// Calls solveForward() with a bit-intersection as join operation.
void solveForwardWithIntersect();
/// Calls solveForward() with a bit-union as join operation.
void solveForwardWithUnion();
/// Derives the block entry sets from the exit sets by applying the gen and
/// kill sets.
/// At control flow joins, the \p join operation is applied.
void solveBackward(JoinOperation join);
/// Calls solveBackward() with a bit-intersection as join operation.
void solveBackwardWithIntersect();
/// Calls solveBackward() with a bit-union as join operation.
void solveBackwardWithUnion();
/// Debug dump the MemoryLifetime internals.
void dump() const;
};
/// Verifies the lifetime of memory locations in a function.
void verifyMemoryLifetime(SILFunction *function);
} // end swift namespace } // end swift namespace
#endif #endif

View File

@@ -1147,6 +1147,9 @@ public:
/// invariants. /// invariants.
void verify(bool SingleFunction = true) const; void verify(bool SingleFunction = true) const;
/// Verifies the lifetime of memory locations in the function.
void verifyMemoryLifetime();
/// Run the SIL ownership verifier to check for ownership invariant failures. /// Run the SIL ownership verifier to check for ownership invariant failures.
/// ///
/// NOTE: The ownership verifier is always run when performing normal IR /// NOTE: The ownership verifier is always run when performing normal IR

View File

@@ -0,0 +1,151 @@
//===--- BitDataflow.cpp --------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 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 "bit-dataflow"
#include "swift/SIL/BitDataflow.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/MemoryLocations.h"
#include "llvm/Support/raw_ostream.h"
using namespace swift;
BitDataflow::BitDataflow(SILFunction *function, unsigned numLocations) :
blockStates(function, [numLocations](SILBasicBlock *block) {
return BlockState(numLocations);
}) {}
void BitDataflow::entryReachabilityAnalysis() {
llvm::SmallVector<SILBasicBlock *, 16> workList;
auto entry = blockStates.entry();
entry.data.reachableFromEntry = true;
workList.push_back(&entry.block);
while (!workList.empty()) {
SILBasicBlock *block = workList.pop_back_val();
for (SILBasicBlock *succ : block->getSuccessorBlocks()) {
BlockState &succState = blockStates[succ];
if (!succState.reachableFromEntry) {
succState.reachableFromEntry = true;
workList.push_back(succ);
}
}
}
}
void BitDataflow::exitReachableAnalysis() {
llvm::SmallVector<SILBasicBlock *, 16> workList;
for (auto bd : blockStates) {
if (bd.block.getTerminator()->isFunctionExiting()) {
bd.data.exitReachability = ExitReachability::ReachesExit;
workList.push_back(&bd.block);
} else if (isa<UnreachableInst>(bd.block.getTerminator())) {
bd.data.exitReachability = ExitReachability::ReachesUnreachable;
workList.push_back(&bd.block);
}
}
while (!workList.empty()) {
SILBasicBlock *block = workList.pop_back_val();
BlockState &state = blockStates[block];
for (SILBasicBlock *pred : block->getPredecessorBlocks()) {
BlockState &predState = blockStates[pred];
if (predState.exitReachability < state.exitReachability) {
// As there are 3 states, each block can be put into the workList 2
// times maximum.
predState.exitReachability = state.exitReachability;
workList.push_back(pred);
}
}
}
}
void BitDataflow::solveForward(JoinOperation join) {
// Pretty standard data flow solving.
bool changed = false;
bool firstRound = true;
do {
changed = false;
for (auto bd : blockStates) {
Bits bits = bd.data.entrySet;
assert(!bits.empty());
for (SILBasicBlock *pred : bd.block.getPredecessorBlocks()) {
join(bits, blockStates[pred].exitSet);
}
if (firstRound || bits != bd.data.entrySet) {
changed = true;
bd.data.entrySet = bits;
bits |= bd.data.genSet;
bits.reset(bd.data.killSet);
bd.data.exitSet = bits;
}
}
firstRound = false;
} while (changed);
}
void BitDataflow::solveForwardWithIntersect() {
solveForward([](Bits &entry, const Bits &predExit){
entry &= predExit;
});
}
void BitDataflow::solveForwardWithUnion() {
solveForward([](Bits &entry, const Bits &predExit){
entry |= predExit;
});
}
void BitDataflow::solveBackward(JoinOperation join) {
// Pretty standard data flow solving.
bool changed = false;
bool firstRound = true;
do {
changed = false;
for (auto bd : llvm::reverse(blockStates)) {
Bits bits = bd.data.exitSet;
assert(!bits.empty());
for (SILBasicBlock *succ : bd.block.getSuccessorBlocks()) {
join(bits, blockStates[succ].entrySet);
}
if (firstRound || bits != bd.data.exitSet) {
changed = true;
bd.data.exitSet = bits;
bits |= bd.data.genSet;
bits.reset(bd.data.killSet);
bd.data.entrySet = bits;
}
}
firstRound = false;
} while (changed);
}
void BitDataflow::solveBackwardWithIntersect() {
solveBackward([](Bits &entry, const Bits &predExit){
entry &= predExit;
});
}
void BitDataflow::solveBackwardWithUnion() {
solveBackward([](Bits &entry, const Bits &predExit){
entry |= predExit;
});
}
void BitDataflow::dump() const {
for (auto bd : blockStates) {
llvm::dbgs() << "bb" << bd.block.getDebugID() << ":\n"
<< " entry: " << bd.data.entrySet << '\n'
<< " gen: " << bd.data.genSet << '\n'
<< " kill: " << bd.data.killSet << '\n'
<< " exit: " << bd.data.exitSet << '\n';
}
}

View File

@@ -1,5 +1,6 @@
target_sources(swiftSIL PRIVATE target_sources(swiftSIL PRIVATE
BasicBlockUtils.cpp BasicBlockUtils.cpp
BitDataflow.cpp
DebugUtils.cpp DebugUtils.cpp
Dominance.cpp Dominance.cpp
DynamicCasts.cpp DynamicCasts.cpp
@@ -7,6 +8,7 @@ target_sources(swiftSIL PRIVATE
InstructionUtils.cpp InstructionUtils.cpp
LoopInfo.cpp LoopInfo.cpp
MemAccessUtils.cpp MemAccessUtils.cpp
MemoryLocations.cpp
OptimizationRemark.cpp OptimizationRemark.cpp
OwnershipUtils.cpp OwnershipUtils.cpp
PrettyStackTrace.cpp PrettyStackTrace.cpp

View File

@@ -0,0 +1,469 @@
//===--- MemoryLocations.cpp ----------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 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-memory-locations"
#include "swift/SIL/MemoryLocations.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/ApplySite.h"
#include "swift/SIL/SILModule.h"
#include "llvm/Support/raw_ostream.h"
using namespace swift;
/// Debug dump a location bit vector.
void swift::printBitsAsArray(llvm::raw_ostream &OS, const SmallBitVector &bits) {
const char *separator = "";
OS << '[';
for (int idx = bits.find_first(); idx >= 0; idx = bits.find_next(idx)) {
OS << separator << idx;
separator = ",";
}
OS << ']';
}
void swift::dumpBits(const SmallBitVector &bits) {
llvm::dbgs() << bits << '\n';
}
namespace swift {
namespace {
//===----------------------------------------------------------------------===//
// Utility functions
//===----------------------------------------------------------------------===//
/// Enlarge the bitset if needed to set the bit with \p idx.
static void setBitAndResize(SmallBitVector &bits, unsigned idx) {
if (bits.size() <= idx)
bits.resize(idx + 1);
bits.set(idx);
}
static bool allUsesInSameBlock(AllocStackInst *ASI) {
SILBasicBlock *BB = ASI->getParent();
int numDeallocStacks = 0;
for (Operand *use : ASI->getUses()) {
SILInstruction *user = use->getUser();
if (isa<DeallocStackInst>(user)) {
++numDeallocStacks;
if (user->getParent() != BB)
return false;
}
}
// In case of an unreachable, the dealloc_stack can be missing. In this
// case we don't treat it as a single-block location.
assert(numDeallocStacks <= 1 &&
"A single-block stack location cannot have multiple deallocations");
return numDeallocStacks == 1;
}
static bool shouldTrackLocation(SILType ty, SILFunction *function) {
// Ignore empty tuples and empty structs.
if (auto tupleTy = ty.getAs<TupleType>()) {
return tupleTy->getNumElements() != 0;
}
if (StructDecl *decl = ty.getStructOrBoundGenericStruct()) {
return decl->getStoredProperties().size() != 0;
}
return true;
}
} // anonymous namespace
} // namespace swift
//===----------------------------------------------------------------------===//
// MemoryLocations members
//===----------------------------------------------------------------------===//
MemoryLocations::Location::Location(SILValue val, unsigned index, int parentIdx) :
representativeValue(val),
parentIdx(parentIdx) {
assert(((parentIdx >= 0) ==
(isa<StructElementAddrInst>(val) || isa<TupleElementAddrInst>(val) ||
isa<InitEnumDataAddrInst>(val) || isa<UncheckedTakeEnumDataAddrInst>(val) ||
isa<InitExistentialAddrInst>(val) || isa<OpenExistentialAddrInst>(val)))
&& "sub-locations can only be introduced with struct/tuple/enum projections");
setBitAndResize(subLocations, index);
setBitAndResize(selfAndParents, index);
}
void MemoryLocations::Location::updateFieldCounters(SILType ty, int increment) {
SILFunction *function = representativeValue->getFunction();
if (shouldTrackLocation(ty, function)) {
numFieldsNotCoveredBySubfields += increment;
if (!ty.isTrivial(*function))
numNonTrivialFieldsNotCovered += increment;
}
}
static SILValue getBaseValue(SILValue addr) {
while (true) {
switch (addr->getKind()) {
case ValueKind::BeginAccessInst:
addr = cast<BeginAccessInst>(addr)->getOperand();
break;
default:
return addr;
}
}
}
int MemoryLocations::getLocationIdx(SILValue addr) const {
auto iter = addr2LocIdx.find(getBaseValue(addr));
if (iter == addr2LocIdx.end())
return -1;
return iter->second;
}
const MemoryLocations::Location *
MemoryLocations::getRootLocation(unsigned index) const {
while (true) {
const Location &loc = locations[index];
if (loc.parentIdx < 0)
return &loc;
index = loc.parentIdx;
}
}
static bool canHandleAllocStack(AllocStackInst *asi) {
assert(asi);
// An alloc_stack with dynamic lifetime set has a lifetime that relies on
// unrelated conditional control flow for correctness. This means that we may
// statically leak along paths that were known by the emitter to never be
// taken if the value is live. So bail since we can't verify this.
if (asi->hasDynamicLifetime())
return false;
// Otherwise we can optimize!
return true;
}
void MemoryLocations::analyzeLocations(SILFunction *function) {
// As we have to limit the set of handled locations to memory, which is
// guaranteed to be not aliased, we currently only handle indirect function
// arguments and alloc_stack locations.
for (SILArgument *arg : function->getArguments()) {
SILFunctionArgument *funcArg = cast<SILFunctionArgument>(arg);
switch (funcArg->getArgumentConvention()) {
case SILArgumentConvention::Indirect_In:
case SILArgumentConvention::Indirect_In_Constant:
case SILArgumentConvention::Indirect_In_Guaranteed:
case SILArgumentConvention::Indirect_Out:
// These are not SIL addresses under -enable-sil-opaque-values
if (!function->getConventions().useLoweredAddresses())
break;
LLVM_FALLTHROUGH;
case SILArgumentConvention::Indirect_Inout:
analyzeLocation(funcArg);
break;
default:
break;
}
}
for (SILBasicBlock &BB : *function) {
for (SILInstruction &I : BB) {
if (auto *ASI = dyn_cast<AllocStackInst>(&I)) {
if (canHandleAllocStack(ASI)) {
if (allUsesInSameBlock(ASI)) {
singleBlockLocations.push_back(ASI);
} else {
analyzeLocation(ASI);
}
}
}
}
}
}
void MemoryLocations::analyzeLocation(SILValue loc) {
SILFunction *function = loc->getFunction();
assert(function && "cannot analyze a SILValue which is not in a function");
// Ignore trivial types to keep the number of locations small. Trivial types
// are not interesting anyway, because such memory locations are not
// destroyed.
if (loc->getType().isTrivial(*function))
return;
if (!shouldTrackLocation(loc->getType(), function))
return;
unsigned currentLocIdx = locations.size();
locations.push_back(Location(loc, currentLocIdx));
SmallVector<SILValue, 8> collectedVals;
SubLocationMap subLocationMap;
if (!analyzeLocationUsesRecursively(loc, currentLocIdx, collectedVals,
subLocationMap)) {
locations.set_size(currentLocIdx);
for (SILValue V : collectedVals) {
addr2LocIdx.erase(V);
}
return;
}
addr2LocIdx[loc] = currentLocIdx;
}
void MemoryLocations::handleSingleBlockLocations(
std::function<void (SILBasicBlock *block)> handlerFunc) {
SILBasicBlock *currentBlock = nullptr;
clear();
// Walk over all collected single-block locations.
for (SingleValueInstruction *SVI : singleBlockLocations) {
// Whenever the parent block changes, process the block's locations.
if (currentBlock && SVI->getParent() != currentBlock) {
handlerFunc(currentBlock);
clear();
}
currentBlock = SVI->getParent();
analyzeLocation(SVI);
}
// Process the last block's locations.
if (currentBlock)
handlerFunc(currentBlock);
clear();
}
const MemoryLocations::Bits &MemoryLocations::getNonTrivialLocations() {
if (nonTrivialLocations.empty()) {
// Compute the bitset lazily.
nonTrivialLocations.resize(getNumLocations());
nonTrivialLocations.reset();
unsigned idx = 0;
for (Location &loc : locations) {
initFieldsCounter(loc);
if (loc.numNonTrivialFieldsNotCovered != 0)
nonTrivialLocations.set(idx);
++idx;
}
}
return nonTrivialLocations;
}
void MemoryLocations::dump() const {
unsigned idx = 0;
for (const Location &loc : locations) {
llvm::dbgs() << "location #" << idx << ": sublocs=" << loc.subLocations
<< ", parent=" << loc.parentIdx
<< ", parentbits=" << loc.selfAndParents
<< ", #f=" << loc.numFieldsNotCoveredBySubfields
<< ", #ntf=" << loc.numNonTrivialFieldsNotCovered
<< ": " << loc.representativeValue;
++idx;
}
}
void MemoryLocations::clear() {
locations.clear();
addr2LocIdx.clear();
nonTrivialLocations.clear();
}
static bool hasInoutArgument(ApplySite AS) {
for (Operand &op : AS.getArgumentOperands()) {
switch (AS.getArgumentConvention(op)) {
case SILArgumentConvention::Indirect_Inout:
case SILArgumentConvention::Indirect_InoutAliasable:
return true;
default:
break;
}
}
return false;
}
bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx,
SmallVectorImpl<SILValue> &collectedVals,
SubLocationMap &subLocationMap) {
for (Operand *use : V->getUses()) {
// We can safely ignore type dependent operands, because the lifetime of a
// type is decoupled from the lifetime of its value. For example, even if
// the result of an open_existential_addr is destroyed its type is still
// valid.
if (use->isTypeDependent())
continue;
SILInstruction *user = use->getUser();
// We only handle addr-instructions which are planned to be used with
// opaque values. We can still consider to support other addr-instructions
// like addr-cast instructions. This somehow depends how opaque values will
// look like.
switch (user->getKind()) {
case SILInstructionKind::StructElementAddrInst: {
auto SEAI = cast<StructElementAddrInst>(user);
if (!analyzeAddrProjection(SEAI, locIdx, SEAI->getFieldIndex(),
collectedVals, subLocationMap))
return false;
break;
}
case SILInstructionKind::TupleElementAddrInst: {
auto *TEAI = cast<TupleElementAddrInst>(user);
if (!analyzeAddrProjection(TEAI, locIdx, TEAI->getFieldIndex(),
collectedVals, subLocationMap))
return false;
break;
}
case SILInstructionKind::BeginAccessInst:
if (!analyzeLocationUsesRecursively(cast<BeginAccessInst>(user), locIdx,
collectedVals, subLocationMap))
return false;
break;
case SILInstructionKind::InitExistentialAddrInst:
case SILInstructionKind::OpenExistentialAddrInst:
case SILInstructionKind::InitEnumDataAddrInst:
case SILInstructionKind::UncheckedTakeEnumDataAddrInst:
if (!handleNonTrivialProjections)
return false;
// The payload is represented as a single sub-location of the enum.
if (!analyzeAddrProjection(cast<SingleValueInstruction>(user), locIdx,
/*fieldNr*/ 0, collectedVals, subLocationMap))
return false;
break;
case SILInstructionKind::PartialApplyInst:
// inout/inout_aliasable conventions means that the argument "escapes".
// This is okay for memory verification, but cannot handled by other
// optimizations, like DestroyHoisting.
if (!handleNonTrivialProjections && hasInoutArgument(ApplySite(user)))
return false;
break;
case SILInstructionKind::InjectEnumAddrInst:
case SILInstructionKind::SelectEnumAddrInst:
case SILInstructionKind::ExistentialMetatypeInst:
case SILInstructionKind::ValueMetatypeInst:
case SILInstructionKind::IsUniqueInst:
case SILInstructionKind::FixLifetimeInst:
case SILInstructionKind::LoadInst:
case SILInstructionKind::StoreInst:
case SILInstructionKind::StoreBorrowInst:
case SILInstructionKind::EndAccessInst:
case SILInstructionKind::LoadBorrowInst:
case SILInstructionKind::DestroyAddrInst:
case SILInstructionKind::CheckedCastAddrBranchInst:
case SILInstructionKind::UncheckedRefCastAddrInst:
case SILInstructionKind::UnconditionalCheckedCastAddrInst:
case SILInstructionKind::ApplyInst:
case SILInstructionKind::TryApplyInst:
case SILInstructionKind::BeginApplyInst:
case SILInstructionKind::DebugValueAddrInst:
case SILInstructionKind::CopyAddrInst:
case SILInstructionKind::YieldInst:
case SILInstructionKind::DeallocStackInst:
case SILInstructionKind::SwitchEnumAddrInst:
case SILInstructionKind::WitnessMethodInst:
break;
default:
return false;
}
}
return true;
}
bool MemoryLocations::analyzeAddrProjection(
SingleValueInstruction *projection, unsigned parentLocIdx,unsigned fieldNr,
SmallVectorImpl<SILValue> &collectedVals, SubLocationMap &subLocationMap) {
if (!shouldTrackLocation(projection->getType(), projection->getFunction()))
return false;
unsigned &subLocIdx = subLocationMap[std::make_pair(parentLocIdx, fieldNr)];
if (subLocIdx == 0) {
subLocIdx = locations.size();
assert(subLocIdx > 0);
locations.push_back(Location(projection, subLocIdx, parentLocIdx));
Location &parentLoc = locations[parentLocIdx];
locations.back().selfAndParents |= parentLoc.selfAndParents;
int idx = (int)parentLocIdx;
do {
Location &loc = locations[idx];
setBitAndResize(loc.subLocations, subLocIdx);
idx = loc.parentIdx;
} while (idx >= 0);
initFieldsCounter(parentLoc);
assert(parentLoc.numFieldsNotCoveredBySubfields >= 1);
parentLoc.updateFieldCounters(projection->getType(), -1);
if (parentLoc.numFieldsNotCoveredBySubfields == 0) {
int idx = (int)parentLocIdx;
do {
Location &loc = locations[idx];
loc.subLocations.reset(parentLocIdx);
idx = loc.parentIdx;
} while (idx >= 0);
}
} else if (!isa<OpenExistentialAddrInst>(projection)) {
Location *loc = &locations[subLocIdx];
if (loc->representativeValue->getType() != projection->getType()) {
assert(isa<InitEnumDataAddrInst>(projection) ||
isa<UncheckedTakeEnumDataAddrInst>(projection) ||
isa<InitExistentialAddrInst>(projection));
// We can only handle a single enum payload type for a location or or a
// single concrete existential type. Mismatching types can have a differnt
// number of (non-trivial) sub-locations and we cannot handle this.
// But we ignore opened existential types, because those cannot have
// sub-locations (there cannot be an address projection on an
// open_existential_addr).
if (!isa<OpenExistentialAddrInst>(loc->representativeValue))
return false;
assert(loc->representativeValue->getType().isOpenedExistential());
loc->representativeValue = projection;
}
}
if (!analyzeLocationUsesRecursively(projection, subLocIdx, collectedVals,
subLocationMap)) {
return false;
}
registerProjection(projection, subLocIdx);
collectedVals.push_back(projection);
return true;
}
void MemoryLocations::initFieldsCounter(Location &loc) {
if (loc.numFieldsNotCoveredBySubfields >= 0)
return;
assert(loc.numNonTrivialFieldsNotCovered < 0);
loc.numFieldsNotCoveredBySubfields = 0;
loc.numNonTrivialFieldsNotCovered = 0;
SILFunction *function = loc.representativeValue->getFunction();
SILType ty = loc.representativeValue->getType();
if (StructDecl *decl = ty.getStructOrBoundGenericStruct()) {
if (decl->isResilient(function->getModule().getSwiftModule(),
function->getResilienceExpansion())) {
loc.numFieldsNotCoveredBySubfields = INT_MAX;
return;
}
SILModule &module = function->getModule();
for (VarDecl *field : decl->getStoredProperties()) {
loc.updateFieldCounters(
ty.getFieldType(field, module, TypeExpansionContext(*function)), +1);
}
return;
}
if (auto tupleTy = ty.getAs<TupleType>()) {
for (unsigned idx = 0, end = tupleTy->getNumElements(); idx < end; ++idx) {
loc.updateFieldCounters(ty.getTupleElementType(idx), +1);
}
return;
}
loc.updateFieldCounters(ty, +1);
}

View File

@@ -1,7 +1,7 @@
target_sources(swiftSIL PRIVATE target_sources(swiftSIL PRIVATE
LoadBorrowImmutabilityChecker.cpp LoadBorrowImmutabilityChecker.cpp
LinearLifetimeChecker.cpp LinearLifetimeChecker.cpp
MemoryLifetime.cpp MemoryLifetimeVerifier.cpp
ReborrowVerifier.cpp ReborrowVerifier.cpp
SILOwnershipVerifier.cpp SILOwnershipVerifier.cpp
SILVerifier.cpp) SILVerifier.cpp)

View File

@@ -1,8 +1,8 @@
//===--- MemoryLifetime.cpp -----------------------------------------------===// //===--- MemoryLifetimeVerifier.cpp ---------------------------------------===//
// //
// This source file is part of the Swift.org open source project // This source file is part of the Swift.org open source project
// //
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception // Licensed under Apache License v2.0 with Runtime Library Exception
// //
// See https://swift.org/LICENSE.txt for license information // See https://swift.org/LICENSE.txt for license information
@@ -10,17 +10,14 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sil-memory-lifetime" #define DEBUG_TYPE "sil-memory-lifetime-verifier"
#include "swift/SIL/MemoryLifetime.h" #include "swift/SIL/MemoryLocations.h"
#include "swift/SIL/SILArgument.h" #include "swift/SIL/BitDataflow.h"
#include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILFunction.h" #include "swift/SIL/SILFunction.h"
#include "swift/SIL/ApplySite.h" #include "swift/SIL/ApplySite.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/BasicBlockBits.h" #include "swift/SIL/BasicBlockBits.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/CommandLine.h" #include "llvm/Support/CommandLine.h"
#include "llvm/Support/raw_ostream.h"
using namespace swift; using namespace swift;
@@ -29,595 +26,8 @@ llvm::cl::opt<bool> DontAbortOnMemoryLifetimeErrors(
llvm::cl::desc("Don't abort compliation if the memory lifetime checker " llvm::cl::desc("Don't abort compliation if the memory lifetime checker "
"detects an error.")); "detects an error."));
/// Debug dump a location bit vector.
void swift::printBitsAsArray(llvm::raw_ostream &OS, const SmallBitVector &bits) {
const char *separator = "";
OS << '[';
for (int idx = bits.find_first(); idx >= 0; idx = bits.find_next(idx)) {
OS << separator << idx;
separator = ",";
}
OS << ']';
}
void swift::dumpBits(const SmallBitVector &bits) {
llvm::dbgs() << bits << '\n';
}
namespace swift {
namespace { namespace {
//===----------------------------------------------------------------------===//
// Utility functions
//===----------------------------------------------------------------------===//
/// Enlarge the bitset if needed to set the bit with \p idx.
static void setBitAndResize(SmallBitVector &bits, unsigned idx) {
if (bits.size() <= idx)
bits.resize(idx + 1);
bits.set(idx);
}
static bool allUsesInSameBlock(AllocStackInst *ASI) {
SILBasicBlock *BB = ASI->getParent();
int numDeallocStacks = 0;
for (Operand *use : ASI->getUses()) {
SILInstruction *user = use->getUser();
if (isa<DeallocStackInst>(user)) {
++numDeallocStacks;
if (user->getParent() != BB)
return false;
}
}
// In case of an unreachable, the dealloc_stack can be missing. In this
// case we don't treat it as a single-block location.
assert(numDeallocStacks <= 1 &&
"A single-block stack location cannot have multiple deallocations");
return numDeallocStacks == 1;
}
static bool shouldTrackLocation(SILType ty, SILFunction *function) {
// Ignore empty tuples and empty structs.
if (auto tupleTy = ty.getAs<TupleType>()) {
return tupleTy->getNumElements() != 0;
}
if (StructDecl *decl = ty.getStructOrBoundGenericStruct()) {
return decl->getStoredProperties().size() != 0;
}
return true;
}
} // anonymous namespace
} // namespace swift
//===----------------------------------------------------------------------===//
// MemoryLocations members
//===----------------------------------------------------------------------===//
MemoryLocations::Location::Location(SILValue val, unsigned index, int parentIdx) :
representativeValue(val),
parentIdx(parentIdx) {
assert(((parentIdx >= 0) ==
(isa<StructElementAddrInst>(val) || isa<TupleElementAddrInst>(val) ||
isa<InitEnumDataAddrInst>(val) || isa<UncheckedTakeEnumDataAddrInst>(val) ||
isa<InitExistentialAddrInst>(val) || isa<OpenExistentialAddrInst>(val)))
&& "sub-locations can only be introduced with struct/tuple/enum projections");
setBitAndResize(subLocations, index);
setBitAndResize(selfAndParents, index);
}
void MemoryLocations::Location::updateFieldCounters(SILType ty, int increment) {
SILFunction *function = representativeValue->getFunction();
if (shouldTrackLocation(ty, function)) {
numFieldsNotCoveredBySubfields += increment;
if (!ty.isTrivial(*function))
numNonTrivialFieldsNotCovered += increment;
}
}
static SILValue getBaseValue(SILValue addr) {
while (true) {
switch (addr->getKind()) {
case ValueKind::BeginAccessInst:
addr = cast<BeginAccessInst>(addr)->getOperand();
break;
default:
return addr;
}
}
}
int MemoryLocations::getLocationIdx(SILValue addr) const {
auto iter = addr2LocIdx.find(getBaseValue(addr));
if (iter == addr2LocIdx.end())
return -1;
return iter->second;
}
const MemoryLocations::Location *
MemoryLocations::getRootLocation(unsigned index) const {
while (true) {
const Location &loc = locations[index];
if (loc.parentIdx < 0)
return &loc;
index = loc.parentIdx;
}
}
static bool canHandleAllocStack(AllocStackInst *asi) {
assert(asi);
// An alloc_stack with dynamic lifetime set has a lifetime that relies on
// unrelated conditional control flow for correctness. This means that we may
// statically leak along paths that were known by the emitter to never be
// taken if the value is live. So bail since we can't verify this.
if (asi->hasDynamicLifetime())
return false;
// Otherwise we can optimize!
return true;
}
void MemoryLocations::analyzeLocations(SILFunction *function) {
// As we have to limit the set of handled locations to memory, which is
// guaranteed to be not aliased, we currently only handle indirect function
// arguments and alloc_stack locations.
for (SILArgument *arg : function->getArguments()) {
SILFunctionArgument *funcArg = cast<SILFunctionArgument>(arg);
switch (funcArg->getArgumentConvention()) {
case SILArgumentConvention::Indirect_In:
case SILArgumentConvention::Indirect_In_Constant:
case SILArgumentConvention::Indirect_In_Guaranteed:
case SILArgumentConvention::Indirect_Out:
// These are not SIL addresses under -enable-sil-opaque-values
if (!function->getConventions().useLoweredAddresses())
break;
LLVM_FALLTHROUGH;
case SILArgumentConvention::Indirect_Inout:
analyzeLocation(funcArg);
break;
default:
break;
}
}
for (SILBasicBlock &BB : *function) {
for (SILInstruction &I : BB) {
if (auto *ASI = dyn_cast<AllocStackInst>(&I)) {
if (canHandleAllocStack(ASI)) {
if (allUsesInSameBlock(ASI)) {
singleBlockLocations.push_back(ASI);
} else {
analyzeLocation(ASI);
}
}
}
}
}
}
void MemoryLocations::analyzeLocation(SILValue loc) {
SILFunction *function = loc->getFunction();
assert(function && "cannot analyze a SILValue which is not in a function");
// Ignore trivial types to keep the number of locations small. Trivial types
// are not interesting anyway, because such memory locations are not
// destroyed.
if (loc->getType().isTrivial(*function))
return;
if (!shouldTrackLocation(loc->getType(), function))
return;
unsigned currentLocIdx = locations.size();
locations.push_back(Location(loc, currentLocIdx));
SmallVector<SILValue, 8> collectedVals;
SubLocationMap subLocationMap;
if (!analyzeLocationUsesRecursively(loc, currentLocIdx, collectedVals,
subLocationMap)) {
locations.set_size(currentLocIdx);
for (SILValue V : collectedVals) {
addr2LocIdx.erase(V);
}
return;
}
addr2LocIdx[loc] = currentLocIdx;
}
void MemoryLocations::handleSingleBlockLocations(
std::function<void (SILBasicBlock *block)> handlerFunc) {
SILBasicBlock *currentBlock = nullptr;
clear();
// Walk over all collected single-block locations.
for (SingleValueInstruction *SVI : singleBlockLocations) {
// Whenever the parent block changes, process the block's locations.
if (currentBlock && SVI->getParent() != currentBlock) {
handlerFunc(currentBlock);
clear();
}
currentBlock = SVI->getParent();
analyzeLocation(SVI);
}
// Process the last block's locations.
if (currentBlock)
handlerFunc(currentBlock);
clear();
}
const MemoryLocations::Bits &MemoryLocations::getNonTrivialLocations() {
if (nonTrivialLocations.empty()) {
// Compute the bitset lazily.
nonTrivialLocations.resize(getNumLocations());
nonTrivialLocations.reset();
unsigned idx = 0;
for (Location &loc : locations) {
initFieldsCounter(loc);
if (loc.numNonTrivialFieldsNotCovered != 0)
nonTrivialLocations.set(idx);
++idx;
}
}
return nonTrivialLocations;
}
void MemoryLocations::dump() const {
unsigned idx = 0;
for (const Location &loc : locations) {
llvm::dbgs() << "location #" << idx << ": sublocs=" << loc.subLocations
<< ", parent=" << loc.parentIdx
<< ", parentbits=" << loc.selfAndParents
<< ", #f=" << loc.numFieldsNotCoveredBySubfields
<< ", #ntf=" << loc.numNonTrivialFieldsNotCovered
<< ": " << loc.representativeValue;
++idx;
}
}
void MemoryLocations::clear() {
locations.clear();
addr2LocIdx.clear();
nonTrivialLocations.clear();
}
static bool hasInoutArgument(ApplySite AS) {
for (Operand &op : AS.getArgumentOperands()) {
switch (AS.getArgumentConvention(op)) {
case SILArgumentConvention::Indirect_Inout:
case SILArgumentConvention::Indirect_InoutAliasable:
return true;
default:
break;
}
}
return false;
}
bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx,
SmallVectorImpl<SILValue> &collectedVals,
SubLocationMap &subLocationMap) {
for (Operand *use : V->getUses()) {
// We can safely ignore type dependent operands, because the lifetime of a
// type is decoupled from the lifetime of its value. For example, even if
// the result of an open_existential_addr is destroyed its type is still
// valid.
if (use->isTypeDependent())
continue;
SILInstruction *user = use->getUser();
// We only handle addr-instructions which are planned to be used with
// opaque values. We can still consider to support other addr-instructions
// like addr-cast instructions. This somehow depends how opaque values will
// look like.
switch (user->getKind()) {
case SILInstructionKind::StructElementAddrInst: {
auto SEAI = cast<StructElementAddrInst>(user);
if (!analyzeAddrProjection(SEAI, locIdx, SEAI->getFieldIndex(),
collectedVals, subLocationMap))
return false;
break;
}
case SILInstructionKind::TupleElementAddrInst: {
auto *TEAI = cast<TupleElementAddrInst>(user);
if (!analyzeAddrProjection(TEAI, locIdx, TEAI->getFieldIndex(),
collectedVals, subLocationMap))
return false;
break;
}
case SILInstructionKind::BeginAccessInst:
if (!analyzeLocationUsesRecursively(cast<BeginAccessInst>(user), locIdx,
collectedVals, subLocationMap))
return false;
break;
case SILInstructionKind::InitExistentialAddrInst:
case SILInstructionKind::OpenExistentialAddrInst:
case SILInstructionKind::InitEnumDataAddrInst:
case SILInstructionKind::UncheckedTakeEnumDataAddrInst:
if (!handleNonTrivialProjections)
return false;
// The payload is represented as a single sub-location of the enum.
if (!analyzeAddrProjection(cast<SingleValueInstruction>(user), locIdx,
/*fieldNr*/ 0, collectedVals, subLocationMap))
return false;
break;
case SILInstructionKind::PartialApplyInst:
// inout/inout_aliasable conventions means that the argument "escapes".
// This is okay for memory verification, but cannot handled by other
// optimizations, like DestroyHoisting.
if (!handleNonTrivialProjections && hasInoutArgument(ApplySite(user)))
return false;
break;
case SILInstructionKind::InjectEnumAddrInst:
case SILInstructionKind::SelectEnumAddrInst:
case SILInstructionKind::ExistentialMetatypeInst:
case SILInstructionKind::ValueMetatypeInst:
case SILInstructionKind::IsUniqueInst:
case SILInstructionKind::FixLifetimeInst:
case SILInstructionKind::LoadInst:
case SILInstructionKind::StoreInst:
case SILInstructionKind::StoreBorrowInst:
case SILInstructionKind::EndAccessInst:
case SILInstructionKind::LoadBorrowInst:
case SILInstructionKind::DestroyAddrInst:
case SILInstructionKind::CheckedCastAddrBranchInst:
case SILInstructionKind::UncheckedRefCastAddrInst:
case SILInstructionKind::UnconditionalCheckedCastAddrInst:
case SILInstructionKind::ApplyInst:
case SILInstructionKind::TryApplyInst:
case SILInstructionKind::BeginApplyInst:
case SILInstructionKind::DebugValueAddrInst:
case SILInstructionKind::CopyAddrInst:
case SILInstructionKind::YieldInst:
case SILInstructionKind::DeallocStackInst:
case SILInstructionKind::SwitchEnumAddrInst:
case SILInstructionKind::WitnessMethodInst:
break;
default:
return false;
}
}
return true;
}
bool MemoryLocations::analyzeAddrProjection(
SingleValueInstruction *projection, unsigned parentLocIdx,unsigned fieldNr,
SmallVectorImpl<SILValue> &collectedVals, SubLocationMap &subLocationMap) {
if (!shouldTrackLocation(projection->getType(), projection->getFunction()))
return false;
unsigned &subLocIdx = subLocationMap[std::make_pair(parentLocIdx, fieldNr)];
if (subLocIdx == 0) {
subLocIdx = locations.size();
assert(subLocIdx > 0);
locations.push_back(Location(projection, subLocIdx, parentLocIdx));
Location &parentLoc = locations[parentLocIdx];
locations.back().selfAndParents |= parentLoc.selfAndParents;
int idx = (int)parentLocIdx;
do {
Location &loc = locations[idx];
setBitAndResize(loc.subLocations, subLocIdx);
idx = loc.parentIdx;
} while (idx >= 0);
initFieldsCounter(parentLoc);
assert(parentLoc.numFieldsNotCoveredBySubfields >= 1);
parentLoc.updateFieldCounters(projection->getType(), -1);
if (parentLoc.numFieldsNotCoveredBySubfields == 0) {
int idx = (int)parentLocIdx;
do {
Location &loc = locations[idx];
loc.subLocations.reset(parentLocIdx);
idx = loc.parentIdx;
} while (idx >= 0);
}
} else if (!isa<OpenExistentialAddrInst>(projection)) {
Location *loc = &locations[subLocIdx];
if (loc->representativeValue->getType() != projection->getType()) {
assert(isa<InitEnumDataAddrInst>(projection) ||
isa<UncheckedTakeEnumDataAddrInst>(projection) ||
isa<InitExistentialAddrInst>(projection));
// We can only handle a single enum payload type for a location or or a
// single concrete existential type. Mismatching types can have a differnt
// number of (non-trivial) sub-locations and we cannot handle this.
// But we ignore opened existential types, because those cannot have
// sub-locations (there cannot be an address projection on an
// open_existential_addr).
if (!isa<OpenExistentialAddrInst>(loc->representativeValue))
return false;
assert(loc->representativeValue->getType().isOpenedExistential());
loc->representativeValue = projection;
}
}
if (!analyzeLocationUsesRecursively(projection, subLocIdx, collectedVals,
subLocationMap)) {
return false;
}
registerProjection(projection, subLocIdx);
collectedVals.push_back(projection);
return true;
}
void MemoryLocations::initFieldsCounter(Location &loc) {
if (loc.numFieldsNotCoveredBySubfields >= 0)
return;
assert(loc.numNonTrivialFieldsNotCovered < 0);
loc.numFieldsNotCoveredBySubfields = 0;
loc.numNonTrivialFieldsNotCovered = 0;
SILFunction *function = loc.representativeValue->getFunction();
SILType ty = loc.representativeValue->getType();
if (StructDecl *decl = ty.getStructOrBoundGenericStruct()) {
if (decl->isResilient(function->getModule().getSwiftModule(),
function->getResilienceExpansion())) {
loc.numFieldsNotCoveredBySubfields = INT_MAX;
return;
}
SILModule &module = function->getModule();
for (VarDecl *field : decl->getStoredProperties()) {
loc.updateFieldCounters(
ty.getFieldType(field, module, TypeExpansionContext(*function)), +1);
}
return;
}
if (auto tupleTy = ty.getAs<TupleType>()) {
for (unsigned idx = 0, end = tupleTy->getNumElements(); idx < end; ++idx) {
loc.updateFieldCounters(ty.getTupleElementType(idx), +1);
}
return;
}
loc.updateFieldCounters(ty, +1);
}
//===----------------------------------------------------------------------===//
// MemoryDataflow members
//===----------------------------------------------------------------------===//
MemoryDataflow::MemoryDataflow(SILFunction *function, unsigned numLocations) :
blockStates(function, [numLocations](SILBasicBlock *block) {
return BlockState(numLocations);
}) {}
void MemoryDataflow::entryReachabilityAnalysis() {
llvm::SmallVector<SILBasicBlock *, 16> workList;
auto entry = blockStates.entry();
entry.data.reachableFromEntry = true;
workList.push_back(&entry.block);
while (!workList.empty()) {
SILBasicBlock *block = workList.pop_back_val();
for (SILBasicBlock *succ : block->getSuccessorBlocks()) {
BlockState &succState = blockStates[succ];
if (!succState.reachableFromEntry) {
succState.reachableFromEntry = true;
workList.push_back(succ);
}
}
}
}
void MemoryDataflow::exitReachableAnalysis() {
llvm::SmallVector<SILBasicBlock *, 16> workList;
for (auto bd : blockStates) {
if (bd.block.getTerminator()->isFunctionExiting()) {
bd.data.exitReachability = ExitReachability::ReachesExit;
workList.push_back(&bd.block);
} else if (isa<UnreachableInst>(bd.block.getTerminator())) {
bd.data.exitReachability = ExitReachability::ReachesUnreachable;
workList.push_back(&bd.block);
}
}
while (!workList.empty()) {
SILBasicBlock *block = workList.pop_back_val();
BlockState &state = blockStates[block];
for (SILBasicBlock *pred : block->getPredecessorBlocks()) {
BlockState &predState = blockStates[pred];
if (predState.exitReachability < state.exitReachability) {
// As there are 3 states, each block can be put into the workList 2
// times maximum.
predState.exitReachability = state.exitReachability;
workList.push_back(pred);
}
}
}
}
void MemoryDataflow::solveForward(JoinOperation join) {
// Pretty standard data flow solving.
bool changed = false;
bool firstRound = true;
do {
changed = false;
for (auto bd : blockStates) {
Bits bits = bd.data.entrySet;
assert(!bits.empty());
for (SILBasicBlock *pred : bd.block.getPredecessorBlocks()) {
join(bits, blockStates[pred].exitSet);
}
if (firstRound || bits != bd.data.entrySet) {
changed = true;
bd.data.entrySet = bits;
bits |= bd.data.genSet;
bits.reset(bd.data.killSet);
bd.data.exitSet = bits;
}
}
firstRound = false;
} while (changed);
}
void MemoryDataflow::solveForwardWithIntersect() {
solveForward([](Bits &entry, const Bits &predExit){
entry &= predExit;
});
}
void MemoryDataflow::solveForwardWithUnion() {
solveForward([](Bits &entry, const Bits &predExit){
entry |= predExit;
});
}
void MemoryDataflow::solveBackward(JoinOperation join) {
// Pretty standard data flow solving.
bool changed = false;
bool firstRound = true;
do {
changed = false;
for (auto bd : llvm::reverse(blockStates)) {
Bits bits = bd.data.exitSet;
assert(!bits.empty());
for (SILBasicBlock *succ : bd.block.getSuccessorBlocks()) {
join(bits, blockStates[succ].entrySet);
}
if (firstRound || bits != bd.data.exitSet) {
changed = true;
bd.data.exitSet = bits;
bits |= bd.data.genSet;
bits.reset(bd.data.killSet);
bd.data.entrySet = bits;
}
}
firstRound = false;
} while (changed);
}
void MemoryDataflow::solveBackwardWithIntersect() {
solveBackward([](Bits &entry, const Bits &predExit){
entry &= predExit;
});
}
void MemoryDataflow::solveBackwardWithUnion() {
solveBackward([](Bits &entry, const Bits &predExit){
entry |= predExit;
});
}
void MemoryDataflow::dump() const {
for (auto bd : blockStates) {
llvm::dbgs() << "bb" << bd.block.getDebugID() << ":\n"
<< " entry: " << bd.data.entrySet << '\n'
<< " gen: " << bd.data.genSet << '\n'
<< " kill: " << bd.data.killSet << '\n'
<< " exit: " << bd.data.exitSet << '\n';
}
}
//===----------------------------------------------------------------------===//
// MemoryLifetimeVerifier
//===----------------------------------------------------------------------===//
/// A utility for verifying memory lifetime. /// A utility for verifying memory lifetime.
/// ///
/// The MemoryLifetime utility checks the lifetime of memory locations. /// The MemoryLifetime utility checks the lifetime of memory locations.
@@ -630,7 +40,7 @@ class MemoryLifetimeVerifier {
using Bits = MemoryLocations::Bits; using Bits = MemoryLocations::Bits;
using Location = MemoryLocations::Location; using Location = MemoryLocations::Location;
using BlockState = MemoryDataflow::BlockState; using BlockState = BitDataflow::BlockState;
SILFunction *function; SILFunction *function;
MemoryLocations locations; MemoryLocations locations;
@@ -689,7 +99,7 @@ class MemoryLifetimeVerifier {
void setBitsOfPredecessor(Bits &genSet, Bits &killSet, SILBasicBlock *block); void setBitsOfPredecessor(Bits &genSet, Bits &killSet, SILBasicBlock *block);
/// Initializes the data flow bits sets in the block states for all blocks. /// Initializes the data flow bits sets in the block states for all blocks.
void initDataflow(MemoryDataflow &dataFlow); void initDataflow(BitDataflow &dataFlow);
/// Initializes the data flow bits sets in the block state for a single block. /// Initializes the data flow bits sets in the block state for a single block.
void initDataflowInBlock(SILBasicBlock *block, BlockState &state); void initDataflowInBlock(SILBasicBlock *block, BlockState &state);
@@ -700,7 +110,7 @@ class MemoryLifetimeVerifier {
bool isTryApply); bool isTryApply);
/// Perform all checks in the function after the data flow has been computed. /// Perform all checks in the function after the data flow has been computed.
void checkFunction(MemoryDataflow &dataFlow); void checkFunction(BitDataflow &dataFlow);
/// Check all instructions in \p block, starting with \p bits as entry set. /// Check all instructions in \p block, starting with \p bits as entry set.
void checkBlock(SILBasicBlock *block, Bits &bits); void checkBlock(SILBasicBlock *block, Bits &bits);
@@ -711,6 +121,16 @@ class MemoryLifetimeVerifier {
SILArgumentConvention argumentConvention, SILArgumentConvention argumentConvention,
SILInstruction *applyInst); SILInstruction *applyInst);
// Utility functions for setting and clearing gen- and kill-bits.
void genBits(BitDataflow::BlockState &blockState, SILValue addr) {
locations.genBits(blockState.genSet, blockState.killSet, addr);
}
void killBits(BitDataflow::BlockState &blockState, SILValue addr) {
locations.killBits(blockState.genSet, blockState.killSet, addr);
}
public: public:
MemoryLifetimeVerifier(SILFunction *function) : MemoryLifetimeVerifier(SILFunction *function) :
function(function), locations(/*handleNonTrivialProjections*/ true) {} function(function), locations(/*handleNonTrivialProjections*/ true) {}
@@ -857,7 +277,7 @@ void MemoryLifetimeVerifier::registerStoreBorrowLocation(SILValue addr) {
} }
} }
void MemoryLifetimeVerifier::initDataflow(MemoryDataflow &dataFlow) { void MemoryLifetimeVerifier::initDataflow(BitDataflow &dataFlow) {
// Initialize the entry and exit sets to all-bits-set. Except for the function // Initialize the entry and exit sets to all-bits-set. Except for the function
// entry. // entry.
for (auto bs : dataFlow) { for (auto bs : dataFlow) {
@@ -896,7 +316,7 @@ void MemoryLifetimeVerifier::initDataflowInBlock(SILBasicBlock *block,
auto *LI = cast<LoadInst>(&I); auto *LI = cast<LoadInst>(&I);
switch (LI->getOwnershipQualifier()) { switch (LI->getOwnershipQualifier()) {
case LoadOwnershipQualifier::Take: case LoadOwnershipQualifier::Take:
state.killBits(LI->getOperand(), locations); killBits(state, LI->getOperand());
break; break;
default: default:
break; break;
@@ -904,19 +324,19 @@ void MemoryLifetimeVerifier::initDataflowInBlock(SILBasicBlock *block,
break; break;
} }
case SILInstructionKind::StoreInst: case SILInstructionKind::StoreInst:
state.genBits(cast<StoreInst>(&I)->getDest(), locations); genBits(state, cast<StoreInst>(&I)->getDest());
break; break;
case SILInstructionKind::StoreBorrowInst: { case SILInstructionKind::StoreBorrowInst: {
SILValue destAddr = cast<StoreBorrowInst>(&I)->getDest(); SILValue destAddr = cast<StoreBorrowInst>(&I)->getDest();
state.genBits(destAddr, locations); genBits(state, destAddr);
registerStoreBorrowLocation(destAddr); registerStoreBorrowLocation(destAddr);
break; break;
} }
case SILInstructionKind::CopyAddrInst: { case SILInstructionKind::CopyAddrInst: {
auto *CAI = cast<CopyAddrInst>(&I); auto *CAI = cast<CopyAddrInst>(&I);
if (CAI->isTakeOfSrc()) if (CAI->isTakeOfSrc())
state.killBits(CAI->getSrc(), locations); killBits(state, CAI->getSrc());
state.genBits(CAI->getDest(), locations); genBits(state, CAI->getDest());
break; break;
} }
case SILInstructionKind::InjectEnumAddrInst: { case SILInstructionKind::InjectEnumAddrInst: {
@@ -926,20 +346,20 @@ void MemoryLifetimeVerifier::initDataflowInBlock(SILBasicBlock *block,
// This is a bit tricky: an injected no-payload case means that the // This is a bit tricky: an injected no-payload case means that the
// "full" enum is initialized. So, for the purpose of dataflow, we // "full" enum is initialized. So, for the purpose of dataflow, we
// treat it like a full initialization of the payload data. // treat it like a full initialization of the payload data.
state.genBits(IEAI->getOperand(), locations); genBits(state, IEAI->getOperand());
} }
break; break;
} }
case SILInstructionKind::DestroyAddrInst: case SILInstructionKind::DestroyAddrInst:
case SILInstructionKind::DeallocStackInst: case SILInstructionKind::DeallocStackInst:
state.killBits(I.getOperand(0), locations); killBits(state, I.getOperand(0));
break; break;
case SILInstructionKind::UncheckedRefCastAddrInst: case SILInstructionKind::UncheckedRefCastAddrInst:
case SILInstructionKind::UnconditionalCheckedCastAddrInst: { case SILInstructionKind::UnconditionalCheckedCastAddrInst: {
SILValue src = I.getOperand(CopyLikeInstruction::Src); SILValue src = I.getOperand(CopyLikeInstruction::Src);
SILValue dest = I.getOperand(CopyLikeInstruction::Dest); SILValue dest = I.getOperand(CopyLikeInstruction::Dest);
state.killBits(src, locations); killBits(state, src);
state.genBits(dest, locations); genBits(state, dest);
break; break;
} }
case SILInstructionKind::PartialApplyInst: case SILInstructionKind::PartialApplyInst:
@@ -1013,14 +433,14 @@ void MemoryLifetimeVerifier::setFuncOperandBits(BlockState &state, Operand &op,
switch (convention) { switch (convention) {
case SILArgumentConvention::Indirect_In: case SILArgumentConvention::Indirect_In:
case SILArgumentConvention::Indirect_In_Constant: case SILArgumentConvention::Indirect_In_Constant:
state.killBits(op.get(), locations); killBits(state, op.get());
break; break;
case SILArgumentConvention::Indirect_Out: case SILArgumentConvention::Indirect_Out:
// try_apply is special, because an @out result is only initialized // try_apply is special, because an @out result is only initialized
// in the normal-block, but not in the throw-block. // in the normal-block, but not in the throw-block.
// We handle @out result of try_apply in setBitsOfPredecessor. // We handle the @out result of try_apply in setBitsOfPredecessor.
if (!isTryApply) if (!isTryApply)
state.genBits(op.get(), locations); genBits(state, op.get());
break; break;
case SILArgumentConvention::Indirect_In_Guaranteed: case SILArgumentConvention::Indirect_In_Guaranteed:
case SILArgumentConvention::Indirect_Inout: case SILArgumentConvention::Indirect_Inout:
@@ -1032,7 +452,7 @@ void MemoryLifetimeVerifier::setFuncOperandBits(BlockState &state, Operand &op,
} }
} }
void MemoryLifetimeVerifier::checkFunction(MemoryDataflow &dataFlow) { void MemoryLifetimeVerifier::checkFunction(BitDataflow &dataFlow) {
// Collect the bits which we require to be set at function exits. // Collect the bits which we require to be set at function exits.
Bits expectedReturnBits(locations.getNumLocations()); Bits expectedReturnBits(locations.getNumLocations());
@@ -1296,7 +716,7 @@ void MemoryLifetimeVerifier::verify() {
// blocks. // blocks.
locations.analyzeLocations(function); locations.analyzeLocations(function);
if (locations.getNumLocations() > 0) { if (locations.getNumLocations() > 0) {
MemoryDataflow dataFlow(function, locations.getNumLocations()); BitDataflow dataFlow(function, locations.getNumLocations());
dataFlow.entryReachabilityAnalysis(); dataFlow.entryReachabilityAnalysis();
dataFlow.exitReachableAnalysis(); dataFlow.exitReachableAnalysis();
initDataflow(dataFlow); initDataflow(dataFlow);
@@ -1311,7 +731,9 @@ void MemoryLifetimeVerifier::verify() {
}); });
} }
void swift::verifyMemoryLifetime(SILFunction *function) { } // anonymous namespace
MemoryLifetimeVerifier verifier(function);
void SILFunction::verifyMemoryLifetime() {
MemoryLifetimeVerifier verifier(this);
verifier.verify(); verifier.verify();
} }

View File

@@ -29,7 +29,6 @@
#include "swift/SIL/Dominance.h" #include "swift/SIL/Dominance.h"
#include "swift/SIL/DynamicCasts.h" #include "swift/SIL/DynamicCasts.h"
#include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/MemAccessUtils.h"
#include "swift/SIL/MemoryLifetime.h"
#include "swift/SIL/OwnershipUtils.h" #include "swift/SIL/OwnershipUtils.h"
#include "swift/SIL/PostOrder.h" #include "swift/SIL/PostOrder.h"
#include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/PrettyStackTrace.h"
@@ -5624,7 +5623,7 @@ public:
if (F->hasOwnership() && F->shouldVerifyOwnership() && if (F->hasOwnership() && F->shouldVerifyOwnership() &&
!F->getModule().getASTContext().hadError()) { !F->getModule().getASTContext().hadError()) {
verifyMemoryLifetime(F); F->verifyMemoryLifetime();
} }
} }

View File

@@ -14,7 +14,7 @@
#include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILFunction.h" #include "swift/SIL/SILFunction.h"
#include "swift/SIL/ApplySite.h" #include "swift/SIL/ApplySite.h"
#include "swift/SIL/MemoryLifetime.h" #include "swift/SIL/MemoryLocations.h"
#include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/MemAccessUtils.h"

View File

@@ -60,7 +60,7 @@
#include "swift/SIL/Projection.h" #include "swift/SIL/Projection.h"
#include "swift/SIL/SILArgument.h" #include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILBuilder.h"
#include "swift/SIL/MemoryLifetime.h" #include "swift/SIL/MemoryLocations.h"
#include "swift/SILOptimizer/Analysis/AliasAnalysis.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h"
#include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h" #include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
#include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Passes.h"

View File

@@ -12,7 +12,8 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sil-destroy-hoisting" #define DEBUG_TYPE "sil-destroy-hoisting"
#include "swift/SIL/MemoryLifetime.h" #include "swift/SIL/MemoryLocations.h"
#include "swift/SIL/BitDataflow.h"
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
#include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/CFGOptUtils.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h"
@@ -65,7 +66,7 @@ namespace {
class DestroyHoisting { class DestroyHoisting {
using Bits = MemoryLocations::Bits; using Bits = MemoryLocations::Bits;
using BlockState = MemoryDataflow::BlockState; using BlockState = BitDataflow::BlockState;
SILFunction *function; SILFunction *function;
MemoryLocations locations; MemoryLocations locations;
@@ -78,14 +79,14 @@ class DestroyHoisting {
DominanceInfo *domTree = nullptr; DominanceInfo *domTree = nullptr;
bool madeChanges = false; bool madeChanges = false;
void expandStores(MemoryDataflow &dataFlow); void expandStores(BitDataflow &dataFlow);
void initDataflow(MemoryDataflow &dataFlow); void initDataflow(BitDataflow &dataFlow);
void initDataflowInBlock(SILBasicBlock *block, BlockState &state); void initDataflowInBlock(SILBasicBlock *block, BlockState &state);
bool canIgnoreUnreachableBlock(SILBasicBlock *block, bool canIgnoreUnreachableBlock(SILBasicBlock *block,
MemoryDataflow &dataFlow); BitDataflow &dataFlow);
int getDestroyedLoc(SILInstruction *I) { int getDestroyedLoc(SILInstruction *I) {
if (auto *DAI = dyn_cast<DestroyAddrInst>(I)) if (auto *DAI = dyn_cast<DestroyAddrInst>(I))
@@ -99,7 +100,7 @@ class DestroyHoisting {
void getUsedLocationsOfInst(Bits &bits, SILInstruction *Inst); void getUsedLocationsOfInst(Bits &bits, SILInstruction *Inst);
void moveDestroys(MemoryDataflow &dataFlow); void moveDestroys(BitDataflow &dataFlow);
bool locationOverlaps(const MemoryLocations::Location *loc, bool locationOverlaps(const MemoryLocations::Location *loc,
const Bits &destroys); const Bits &destroys);
@@ -117,9 +118,9 @@ class DestroyHoisting {
SILValue createAddress(unsigned locIdx, SILBuilder &builder); SILValue createAddress(unsigned locIdx, SILBuilder &builder);
void tailMerging(MemoryDataflow &dataFlow); void tailMerging(BitDataflow &dataFlow);
bool tailMergingInBlock(SILBasicBlock *block, MemoryDataflow &dataFlow); bool tailMergingInBlock(SILBasicBlock *block, BitDataflow &dataFlow);
public: public:
DestroyHoisting(SILFunction *function, DominanceAnalysis *DA) : DestroyHoisting(SILFunction *function, DominanceAnalysis *DA) :
@@ -147,7 +148,7 @@ static bool isExpansionEnabler(SILInstruction *I) {
// This enables the switch-enum optimization (see above). // This enables the switch-enum optimization (see above).
// TODO: investigate the benchmark regressions and enable store-expansion more // TODO: investigate the benchmark regressions and enable store-expansion more
// aggressively. // aggressively.
void DestroyHoisting::expandStores(MemoryDataflow &dataFlow) { void DestroyHoisting::expandStores(BitDataflow &dataFlow) {
Bits usedLocs(locations.getNumLocations()); Bits usedLocs(locations.getNumLocations());
// Initialize the dataflow, which tells us which destroy_addr instructions are // Initialize the dataflow, which tells us which destroy_addr instructions are
@@ -212,7 +213,7 @@ void DestroyHoisting::expandStores(MemoryDataflow &dataFlow) {
} }
// Initialize the dataflow for moving destroys up the control flow. // Initialize the dataflow for moving destroys up the control flow.
void DestroyHoisting::initDataflow(MemoryDataflow &dataFlow) { void DestroyHoisting::initDataflow(BitDataflow &dataFlow) {
for (auto bs : dataFlow) { for (auto bs : dataFlow) {
bs.data.genSet.reset(); bs.data.genSet.reset();
bs.data.killSet.reset(); bs.data.killSet.reset();
@@ -290,7 +291,7 @@ void DestroyHoisting::initDataflowInBlock(SILBasicBlock *block,
// unreachable which does not touch any of our memory locations. We can just // unreachable which does not touch any of our memory locations. We can just
// ignore those blocks. // ignore those blocks.
bool DestroyHoisting::canIgnoreUnreachableBlock(SILBasicBlock *block, bool DestroyHoisting::canIgnoreUnreachableBlock(SILBasicBlock *block,
MemoryDataflow &dataFlow) { BitDataflow &dataFlow) {
assert(isa<UnreachableInst>(block->getTerminator())); assert(isa<UnreachableInst>(block->getTerminator()));
// Is it a single unreachable-block (i.e. it has a single predecessor from // Is it a single unreachable-block (i.e. it has a single predecessor from
@@ -384,7 +385,7 @@ static void processRemoveList(SmallVectorImpl<SILInstruction *> &toRemove) {
} }
// Do the actual moving of destroy_addr instructions. // Do the actual moving of destroy_addr instructions.
void DestroyHoisting::moveDestroys(MemoryDataflow &dataFlow) { void DestroyHoisting::moveDestroys(BitDataflow &dataFlow) {
// Don't eagerly delete destroy_addr instructions, instead put them into this // Don't eagerly delete destroy_addr instructions, instead put them into this
// list. When we are about to "move" a destroy_addr just over some sideeffect- // list. When we are about to "move" a destroy_addr just over some sideeffect-
// free instructions, we'll keep it at the current location. // free instructions, we'll keep it at the current location.
@@ -630,7 +631,7 @@ SILValue DestroyHoisting::createAddress(unsigned locIdx, SILBuilder &builder) {
// destroy_addr %a // will be hoisted (duplicated) into bb2 and bb2 // destroy_addr %a // will be hoisted (duplicated) into bb2 and bb2
// \endcode // \endcode
// This is mainly a code size reduction optimization. // This is mainly a code size reduction optimization.
void DestroyHoisting::tailMerging(MemoryDataflow &dataFlow) { void DestroyHoisting::tailMerging(BitDataflow &dataFlow) {
// TODO: we could do a worklist algorithm here instead of iterating through // TODO: we could do a worklist algorithm here instead of iterating through
// all the function blocks. // all the function blocks.
@@ -644,7 +645,7 @@ void DestroyHoisting::tailMerging(MemoryDataflow &dataFlow) {
} }
bool DestroyHoisting::tailMergingInBlock(SILBasicBlock *block, bool DestroyHoisting::tailMergingInBlock(SILBasicBlock *block,
MemoryDataflow &dataFlow) { BitDataflow &dataFlow) {
if (block->pred_empty() || block->getSinglePredecessorBlock()) if (block->pred_empty() || block->getSinglePredecessorBlock())
return false; return false;
@@ -707,7 +708,7 @@ bool DestroyHoisting::tailMergingInBlock(SILBasicBlock *block,
bool DestroyHoisting::hoistDestroys() { bool DestroyHoisting::hoistDestroys() {
locations.analyzeLocations(function); locations.analyzeLocations(function);
if (locations.getNumLocations() > 0) { if (locations.getNumLocations() > 0) {
MemoryDataflow dataFlow(function, locations.getNumLocations()); BitDataflow dataFlow(function, locations.getNumLocations());
dataFlow.exitReachableAnalysis(); dataFlow.exitReachableAnalysis();
// Step 1: pre-processing: expand store instructions // Step 1: pre-processing: expand store instructions