Skip to content

DefiniteInitialization: use BasicBlockData instead of a map. #35552

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions include/swift/SIL/BasicBlockData.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ namespace swift {
/// long as the invalidation mechanism ensures that the analysis data is not
/// retrieved after the function's block list changed.
///
/// The Vector template argument specifies how the data is stored. By default
/// it's a SmallVector with an inline-size of 4. This avoids memory allocation
/// for about 70% of all functions.
template <typename Data, typename Vector = llvm::SmallVector<Data, 4>>
/// The template argument \p N specifies how many Data elements can be stored
/// inline in the data vector. The default value of 32 avoids malloc for about
/// 90% of all functions.
template <typename Data, unsigned N = 32>
class BasicBlockData {
SILFunction *function;
Vector data;
llvm::SmallVector<Data, N> data;

/// The data is valid if validForBlockOrder matches the function's
/// BlockListChangeIdx.
Expand Down Expand Up @@ -200,6 +200,19 @@ class BasicBlockData {
const Data &operator[] (SILBasicBlock *block) const {
return data[getIndex(block)];
}

/// If \p block is a new block, i.e. created after this BasicBlockData was
/// constructed, creates a new Data with \p init and returns it.
Data &get(SILBasicBlock *block, llvm::function_ref<Data()> init) {
if (block->index < 0) {
assert(validForBlockOrder == function->BlockListChangeIdx &&
"BasicBlockData invalid because the function's block list changed");
validForBlockOrder = ++function->BlockListChangeIdx;
block->index = data.size();
data.push_back(init());
}
return data[getIndex(block)];
}
};

} // end swift namespace
Expand Down
2 changes: 1 addition & 1 deletion include/swift/SIL/SILBasicBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock> {
friend class SILSuccessor;
friend class SILFunction;
friend class SILGlobalVariable;
template <typename Data, typename Vector> friend class BasicBlockData;
template <typename, unsigned> friend class BasicBlockData;
friend class BasicBlockBitfield;

public:
Expand Down
2 changes: 1 addition & 1 deletion include/swift/SIL/SILFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ class SILFunction
friend class SILBasicBlock;
friend class SILModule;
friend class SILFunctionBuilder;
template <typename Data, typename Vector> friend class BasicBlockData;
template <typename, unsigned> friend class BasicBlockData;
friend class BasicBlockBitfield;

/// Module - The SIL module that the function belongs to.
Expand Down
54 changes: 36 additions & 18 deletions lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "swift/AST/Expr.h"
#include "swift/AST/Stmt.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/SIL/BasicBlockData.h"
#include "swift/SIL/SILBitfield.h"
#include "swift/SIL/SILValue.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/SILArgument.h"
Expand Down Expand Up @@ -136,7 +138,12 @@ namespace {
// T,F -> Partial
SmallBitVector Data;
public:
AvailabilitySet(unsigned NumElts) {
AvailabilitySet() {}

AvailabilitySet(unsigned NumElts) { init(NumElts); }

void init(unsigned NumElts) {
Data.set();
Data.resize(NumElts*2, true);
}

Expand Down Expand Up @@ -270,11 +277,15 @@ namespace {
/// plus the information merged-in from the predecessor blocks.
Optional<DIKind> OutSelfInitialized;

LiveOutBlockState(unsigned NumElements)
: HasNonLoadUse(false),
isInWorkList(false),
LocalAvailability(NumElements),
OutAvailability(NumElements) {
LiveOutBlockState() { init(0); }

void init(unsigned NumElements) {
HasNonLoadUse = false;
isInWorkList = false;
LocalAvailability.init(NumElements);
OutAvailability.init(NumElements);
LocalSelfInitialized = None;
OutSelfInitialized = None;
}

/// Sets all unknown elements to not-available.
Expand Down Expand Up @@ -371,9 +382,8 @@ namespace {
DIKind SelfInitialized;
};

} // end anonymous namespace
using BlockStates = BasicBlockData<LiveOutBlockState>;

namespace {
/// LifetimeChecker - This is the main heavy lifting for definite
/// initialization checking of a memory object.
class LifetimeChecker {
Expand All @@ -390,7 +400,8 @@ namespace {
SmallVector<unsigned, 8> NeedsUpdateForInitState;
std::vector<ConditionalDestroy> ConditionalDestroys;

llvm::SmallDenseMap<SILBasicBlock*, LiveOutBlockState, 32> PerBlockInfo;
BlockStates &blockStates;
BasicBlockFlag blockStateInitialized;

/// This is a map of uses that are not loads (i.e., they are Stores,
/// InOutUses, and Escapes), to their entry in Uses.
Expand Down Expand Up @@ -427,7 +438,8 @@ namespace {

public:
LifetimeChecker(const DIMemoryObjectInfo &TheMemory,
DIElementUseInfo &UseInfo);
DIElementUseInfo &UseInfo,
BlockStates &blockStates);

void doIt();

Expand All @@ -436,9 +448,10 @@ namespace {
void emitSelfConsumedDiagnostic(SILInstruction *Inst);

LiveOutBlockState &getBlockInfo(SILBasicBlock *BB) {
return PerBlockInfo
.insert({BB, LiveOutBlockState(TheMemory.getNumElements())})
.first->second;
auto &state = blockStates.get(BB, []() { return LiveOutBlockState(); });
if (!blockStateInitialized.testAndSet(BB))
state.init(TheMemory.getNumElements());
return state;
}

AvailabilitySet getLivenessAtInst(SILInstruction *Inst, unsigned FirstElt,
Expand Down Expand Up @@ -514,10 +527,12 @@ namespace {
} // end anonymous namespace

LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory,
DIElementUseInfo &UseInfo)
DIElementUseInfo &UseInfo,
BlockStates &blockStates)
: F(TheMemory.getFunction()), Module(TheMemory.getModule()),
TheMemory(TheMemory), Uses(UseInfo.Uses),
StoresToSelf(UseInfo.StoresToSelf), Destroys(UseInfo.Releases) {
StoresToSelf(UseInfo.StoresToSelf), Destroys(UseInfo.Releases),
blockStates(blockStates), blockStateInitialized(&F) {

// The first step of processing an element is to collect information about the
// element into data structures we use later.
Expand Down Expand Up @@ -3087,7 +3102,8 @@ bool LifetimeChecker::isInitializedAtUse(const DIMemoryUse &Use,
// Top Level Driver
//===----------------------------------------------------------------------===//

static void processMemoryObject(MarkUninitializedInst *I) {
static void processMemoryObject(MarkUninitializedInst *I,
BlockStates &blockStates) {
LLVM_DEBUG(llvm::dbgs() << "*** Definite Init looking at: " << *I << "\n");
DIMemoryObjectInfo MemInfo(I);

Expand All @@ -3097,7 +3113,7 @@ static void processMemoryObject(MarkUninitializedInst *I) {
// Walk the use list of the pointer, collecting them into the Uses array.
collectDIElementUsesFrom(MemInfo, UseInfo);

LifetimeChecker(MemInfo, UseInfo).doIt();
LifetimeChecker(MemInfo, UseInfo, blockStates).doIt();
}

/// Check that all memory objects that require initialization before use are
Expand All @@ -3108,6 +3124,8 @@ static bool checkDefiniteInitialization(SILFunction &Fn) {
<< Fn.getName() << "\n");
bool Changed = false;

BlockStates blockStates(&Fn);

for (auto &BB : Fn) {
for (auto I = BB.begin(), E = BB.end(); I != E;) {
SILInstruction *Inst = &*I;
Expand All @@ -3119,7 +3137,7 @@ static bool checkDefiniteInitialization(SILFunction &Fn) {
}

// Then process the memory object.
processMemoryObject(MUI);
processMemoryObject(MUI, blockStates);

// Move off of the MUI only after we have processed memory objects. The
// lifetime checker may rewrite instructions, so it is important to not
Expand Down