diff --git a/include/swift/SIL/SILBasicBlock.h b/include/swift/SIL/SILBasicBlock.h index f42c2e9848908..3603f47a943ad 100644 --- a/include/swift/SIL/SILBasicBlock.h +++ b/include/swift/SIL/SILBasicBlock.h @@ -34,6 +34,7 @@ public llvm::ilist_node, public SILAllocated { friend class SILFunction; friend class SILGlobalVariable; template friend class BasicBlockData; + friend class BasicBlockBitfield; public: using InstListType = llvm::iplist; @@ -57,6 +58,25 @@ public llvm::ilist_node, public SILAllocated { /// A value of -1 means that the index is not initialized yet. int index = -1; + /// Custom bits managed by BasicBlockBitfield. + uint32_t customBits = 0; + + /// The BasicBlockBitfield ID of the last initialized bitfield in customBits. + /// Example: + /// + /// Last initialized field: + /// lastInitializedBitfieldID == C.bitfieldID + /// | + /// V + /// customBits: EE DDD C BB AAA + /// 31 ... 0 + /// + /// -> AAA, BB and C are initialized, + /// DD and EEE are uninitialized + /// + /// See also: BasicBlockBitfield::bitfieldID, SILFunction::currentBitfieldID. + uint64_t lastInitializedBitfieldID = 0; + friend struct llvm::ilist_traits; SILBasicBlock() : Parent(nullptr) {} void operator=(const SILBasicBlock &) = delete; diff --git a/include/swift/SIL/SILBitfield.h b/include/swift/SIL/SILBitfield.h new file mode 100644 index 0000000000000..d13ee844320f4 --- /dev/null +++ b/include/swift/SIL/SILBitfield.h @@ -0,0 +1,169 @@ +//===--- SILBitField.h - Defines the bitfield utilities ---------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the BasicBlockBitfield and BasicBlockFlag utilities. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_SILBITFIELD_H +#define SWIFT_SIL_SILBITFIELD_H + +#include "swift/SIL/SILFunction.h" + +namespace swift { + +/// Utility to add a custom bitfield to a function's basic blocks. +/// +/// This can be used by transforms to store temporary flags or tiny values per +/// basic block. +/// It is very efficient: no memory allocation is needed, no hash set or map is +/// needed for lookup and there is no initialization cost (in contrast to +/// BasicBlockData which needs to iterate over all blocks at initialization). +/// +/// Restrictions: +/// * BasicBlockBitfield instances must be allocated and deallocated +/// following a strict stack discipline. This means, it's fine to use them as +/// (or in) local variables in transformations. But it's e.g. not possible to +/// store a BasicBlockBitfield in an Analysis. +/// * The total number of bits which are alive at the same time must not exceed +/// 32. +class BasicBlockBitfield { + /// The bitfield is "added" to the blocks of this function. + SILFunction *function; + + /// A single linked list of currently alive BasicBlockBitfields (oldest is + /// last, newest is first). + /// The head of the list is function->lastAllocatedBitfield. + BasicBlockBitfield *parent; + + /// Initialized with the monotonically increasing currentBitfieldID of the + /// function. + /// Used to check if the bitfield in a block is initialized. + /// If a block's lastInitializedBitfieldID is less than this ID, it means + /// that the bits of that block are not initialized yet. + /// See also: SILBasicBlock::lastInitializedBitfieldID, + /// SILFunction::currentBitfieldID + unsigned bitfieldID; + + short startBit; + short endBit; + uint32_t mask; + +public: + BasicBlockBitfield(SILFunction *function, int size) : + function(function), + parent(function->newestAliveBitfield), + bitfieldID(function->currentBitfieldID), + startBit(parent ? parent->endBit : 0), + endBit(startBit + size), + mask(0xffffffffu >> (32 - size) << startBit) { + assert(size > 0 && "bit field size must be > 0"); + assert(endBit <= 32 && "too many/large bit fields allocated in function"); + assert((!parent || bitfieldID > parent->bitfieldID) && + "BasicBlockBitfield indices are not in order"); + function->newestAliveBitfield = this; + ++function->currentBitfieldID; + assert(function->currentBitfieldID != 0 && "currentBitfieldID overflow"); + } + + ~BasicBlockBitfield() { + assert(function->newestAliveBitfield == this && + "BasicBlockBitfield destructed too early"); + function->newestAliveBitfield = parent; + } + + BasicBlockBitfield(const BasicBlockBitfield &) = delete; + BasicBlockBitfield(BasicBlockBitfield &&) = delete; + BasicBlockBitfield &operator=(const BasicBlockBitfield &) = delete; + BasicBlockBitfield &operator=(BasicBlockBitfield &&) = delete; + + SILFunction *getFunction() const { return function; } + + unsigned get(SILBasicBlock *block) const { + if (bitfieldID > block->lastInitializedBitfieldID) { + // The bitfield is not initialized yet in this block. + return 0; + } + return (block->customBits & mask) >> startBit; + } + + void set(SILBasicBlock *block, unsigned value) { + assert(((value << startBit) & ~mask) == 0 && + "value too large for BasicBlockBitfield"); + unsigned clearMask = mask; + if (bitfieldID > block->lastInitializedBitfieldID) { + + // The bitfield is not initialized yet in this block. + // Initialize the bitfield, and also initialize all parent bitfields, + // which are not initialized, yet. Example: + // + // This field Last initialized field + // | | + // V V + // EE DDD C BB AAA + // + // block->lastInitializedBitfieldID == AAA.bitfieldID + // -> we have to initialize the fields: BB, C, DDD and EE + // + BasicBlockBitfield *bf = parent; + while (bf && bf->bitfieldID > block->lastInitializedBitfieldID) { + clearMask |= bf->mask; + bf = bf->parent; + } + block->lastInitializedBitfieldID = bitfieldID; + } + block->customBits = (block->customBits & ~clearMask) | (value << startBit); + } +}; + +/// A BasicBlockBitfield containing a single bit - a flag. +class BasicBlockFlag { + BasicBlockBitfield bit; + +public: + BasicBlockFlag(SILFunction *function) : bit(function, 1) {} + + SILFunction *getFunction() const { return bit.getFunction(); } + + bool get(SILBasicBlock *block) const { return (bool)bit.get(block); } + + void set(SILBasicBlock *block) { bit.set(block, 1); } + void reset(SILBasicBlock *block) { bit.set(block, 0); } + + /// Sets the flag and returns the old value. + bool testAndSet(SILBasicBlock *block) { + bool oldValue = get(block); + set(block); + return oldValue; + } +}; + +/// A BasicBlockFlag with a set-like API. +class BasicBlockSet { + BasicBlockFlag flag; + +public: + BasicBlockSet(SILFunction *function) : flag(function) {} + + SILFunction *getFunction() const { return flag.getFunction(); } + + bool contains(SILBasicBlock *block) const { return flag.get(block); } + + /// Returns true if \p block was not contained in the set before inserting. + bool insert(SILBasicBlock *block) { return !flag.testAndSet(block); } + + void remove(SILBasicBlock *block) { flag.reset(block); } +}; + +} // namespace swift + +#endif diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index b4ba5e76125d9..a67cf15bd6cfe 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -37,6 +37,7 @@ class SILInstruction; class SILModule; class SILFunctionBuilder; class SILProfiler; +class BasicBlockBitfield; namespace Lowering { class TypeLowering; @@ -148,6 +149,7 @@ class SILFunction friend class SILModule; friend class SILFunctionBuilder; template friend class BasicBlockData; + friend class BasicBlockBitfield; /// Module - The SIL module that the function belongs to. SILModule &Module; @@ -192,6 +194,16 @@ class SILFunction Identifier ObjCReplacementFor; + /// The head of a single-linked list of currently alive BasicBlockBitfield. + BasicBlockBitfield *newestAliveBitfield = nullptr; + + /// A monotonically increasing ID which is incremented whenever a + /// BasicBlockBitfield is constructed. + /// Usually this stays below 1000, so a 32-bit unsigned is more than + /// sufficient. + /// For details see BasicBlockBitfield::bitfieldID; + unsigned currentBitfieldID = 1; + /// The function's set of semantics attributes. /// /// TODO: Why is this using a std::string? Why don't we use uniqued @@ -1012,9 +1024,6 @@ class SILFunction /// Transfer all blocks of \p F into this function, at the begin of the block /// list. void moveAllBlocksFromOtherFunction(SILFunction *F) { - for (SILBasicBlock &block : *F) { - block.index = -1; - } BlockList.splice(begin(), F->BlockList); } @@ -1026,7 +1035,6 @@ class SILFunction assert(otherFunc != this); BlockList.splice(insertPointInThisFunction, otherFunc->BlockList, blockInOtherFunction); - blockInOtherFunction->index = -1; } /// Move block \p BB to immediately before the iterator \p IP. diff --git a/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h b/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h index 5411f7e9fdafb..c66784c473988 100644 --- a/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h +++ b/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h @@ -23,6 +23,7 @@ #define SWIFT_SILOPTIMIZER_UTILS_BASICBLOCKOPTUTILS_H #include "swift/SIL/SILBasicBlock.h" +#include "swift/SIL/SILBitfield.h" #include "swift/SIL/SILCloner.h" #include "swift/SIL/SILInstruction.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" @@ -35,9 +36,11 @@ class SILLoopInfo; /// Compute the set of reachable blocks. class ReachableBlocks { - SmallPtrSet visited; + BasicBlockSet visited; public: + ReachableBlocks(SILFunction *function) : visited(function) {} + /// Invoke \p visitor for each reachable block in \p f in worklist order (at /// least one predecessor has been visited--defs are always visited before /// uses except for phi-type block args). The \p visitor takes a block @@ -45,10 +48,10 @@ class ReachableBlocks { /// continue visiting blocks. /// /// Returns true if all reachable blocks were visited. - bool visit(SILFunction *f, function_ref visitor); + bool visit(function_ref visitor); /// Return true if \p bb has been visited. - bool isVisited(SILBasicBlock *bb) const { return visited.count(bb); } + bool isVisited(SILBasicBlock *bb) const { return visited.contains(bb); } }; /// Remove all instructions in the body of \p bb in safe manner by using diff --git a/include/swift/SILOptimizer/Utils/ValueLifetime.h b/include/swift/SILOptimizer/Utils/ValueLifetime.h index ceac08b5938f0..7e43dfe77415f 100644 --- a/include/swift/SILOptimizer/Utils/ValueLifetime.h +++ b/include/swift/SILOptimizer/Utils/ValueLifetime.h @@ -20,6 +20,7 @@ #include "swift/Basic/STLExtras.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILBitfield.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" namespace swift { @@ -34,7 +35,10 @@ class ValueLifetimeAnalysis { PointerUnion defValue; /// The set of blocks where the value is live. - llvm::SmallSetVector liveBlocks; + llvm::SmallVector liveBlocks; + + /// True for blocks which are in liveBlocks. + BasicBlockFlag inLiveBlocks; /// The set of instructions where the value is used, or the users-list /// provided with the constructor. @@ -66,7 +70,7 @@ class ValueLifetimeAnalysis { /// iterators. template ValueLifetimeAnalysis(SILArgument *def, const RangeTy &useRange) - : defValue(def), userSet() { + : defValue(def), inLiveBlocks(def->getFunction()), userSet() { for (SILInstruction *use : useRange) userSet.insert(use); propagateLiveness(); @@ -75,7 +79,7 @@ class ValueLifetimeAnalysis { ValueLifetimeAnalysis( SILArgument *def, llvm::iterator_range useRange) - : defValue(def), userSet() { + : defValue(def), inLiveBlocks(def->getFunction()), userSet() { for (Operand *use : useRange) userSet.insert(use->getUser()); propagateLiveness(); @@ -84,7 +88,7 @@ class ValueLifetimeAnalysis { template ValueLifetimeAnalysis( SILInstruction *def, const RangeTy &useRange) - : defValue(def), userSet() { + : defValue(def), inLiveBlocks(def->getFunction()), userSet() { for (SILInstruction *use : useRange) userSet.insert(use); propagateLiveness(); @@ -93,7 +97,7 @@ class ValueLifetimeAnalysis { ValueLifetimeAnalysis( SILInstruction *def, llvm::iterator_range useRange) - : defValue(def), userSet() { + : defValue(def), inLiveBlocks(def->getFunction()), userSet() { for (Operand *use : useRange) userSet.insert(use->getUser()); propagateLiveness(); @@ -144,7 +148,7 @@ class ValueLifetimeAnalysis { /// Returns true if the value is alive at the begin of block \p bb. bool isAliveAtBeginOfBlock(SILBasicBlock *bb) { - return liveBlocks.count(bb) && + return inLiveBlocks.get(bb) && (hasUsersBeforeDef || bb != getDefValueParentBlock()); } diff --git a/lib/SIL/IR/SILBasicBlock.cpp b/lib/SIL/IR/SILBasicBlock.cpp index 3cab5b8334746..1f328310c759f 100644 --- a/lib/SIL/IR/SILBasicBlock.cpp +++ b/lib/SIL/IR/SILBasicBlock.cpp @@ -309,6 +309,8 @@ transferNodesFromList(llvm::ilist_traits &SrcTraits, // If splicing blocks not in the same function, update the parent pointers. for (; First != Last; ++First) { First->Parent = Parent; + First->index = -1; + First->lastInitializedBitfieldID = 0; for (auto &II : *First) II.setDebugScope(ScopeCloner.getOrCreateClonedScope(II.getDebugScope())); } diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index ad973cbce5e9a..3704620a071b7 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -210,6 +210,8 @@ SILFunction::~SILFunction() { assert(RefCount == 0 && "Function cannot be deleted while function_ref's still exist"); + assert(!newestAliveBitfield && + "Not all BasicBlockBitfields deleted at function destruction"); } void SILFunction::createProfiler(ASTNode Root, SILDeclRef forDecl, diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 35b2283d8ef29..1f36e4d6156fa 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -1619,8 +1619,8 @@ void EscapeAnalysis::ConnectionGraph::verify() const { // Verify that all pointer nodes are still mapped, otherwise the process of // merging nodes may have lost information. Only visit reachable blocks, // because the graph builder only mapped values from reachable blocks. - ReachableBlocks reachable; - reachable.visit(F, [this](SILBasicBlock *bb) { + ReachableBlocks reachable(F); + reachable.visit([this](SILBasicBlock *bb) { for (auto &i : *bb) { if (isNonWritableMemoryAddress(&i)) continue; @@ -1748,8 +1748,8 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, assert(ConGraph->isEmpty()); // Visit the blocks in dominance order. - ReachableBlocks reachable; - reachable.visit(ConGraph->F, [&](SILBasicBlock *bb) { + ReachableBlocks reachable(ConGraph->F); + reachable.visit([&](SILBasicBlock *bb) { // Create edges for the instructions. for (auto &i : *bb) { analyzeInstruction(&i, FInfo, BottomUpOrder, RecursionDepth); diff --git a/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp b/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp index 6aab8b55eebf9..55afe6222f876 100644 --- a/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp +++ b/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp @@ -360,8 +360,8 @@ void ArrayInfo::getLastDestroys( // copy_values, using ValueLifetimeAnalysis. The frontier is a list of // instructions that mark the exits of the flow of control from the // \c destroys. - ValueLifetimeAnalysis lifetimeAnalysis = - ValueLifetimeAnalysis(arrayValue->getDefiningInstruction(), destroys); + ValueLifetimeAnalysis lifetimeAnalysis(arrayValue->getDefiningInstruction(), + destroys); ValueLifetimeAnalysis::Frontier frontier; lifetimeAnalysis.computeFrontier(frontier, ValueLifetimeAnalysis::DontModifyCFG); diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index edf912af8c172..348bfdd7dd0df 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -881,8 +881,7 @@ getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun, SILInstruction *valueDefinition = value->getDefiningInstruction(); SILInstruction *def = valueDefinition ? valueDefinition : &(value->getParentBlock()->front()); - ValueLifetimeAnalysis lifetimeAnalysis = - ValueLifetimeAnalysis(def, transitiveUsers); + ValueLifetimeAnalysis lifetimeAnalysis(def, transitiveUsers); ValueLifetimeAnalysis::Frontier frontier; bool hasCriticlEdges = lifetimeAnalysis.computeFrontier( frontier, ValueLifetimeAnalysis::DontModifyCFG); diff --git a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp index 04ad80058b66c..f202a401bee01 100644 --- a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp +++ b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp @@ -19,13 +19,10 @@ using namespace swift; /// Invoke \p visitor for each reachable block in \p f in worklist order (at /// least one predecessor has been visited). -bool ReachableBlocks::visit(SILFunction *f, - function_ref visitor) { - assert(visited.empty() && "blocks already visited"); - +bool ReachableBlocks::visit(function_ref visitor) { // Walk over the CFG, starting at the entry block, until all reachable blocks // are visited. - SILBasicBlock *entryBB = f->getEntryBlock(); + SILBasicBlock *entryBB = visited.getFunction()->getEntryBlock(); SmallVector worklist = {entryBB}; visited.insert(entryBB); while (!worklist.empty()) { @@ -34,7 +31,7 @@ bool ReachableBlocks::visit(SILFunction *f, return false; for (auto &succ : bb->getSuccessors()) { - if (visited.insert(succ).second) + if (visited.insert(succ)) worklist.push_back(succ); } } @@ -71,9 +68,9 @@ void swift::removeDeadBlock(SILBasicBlock *bb) { } bool swift::removeUnreachableBlocks(SILFunction &f) { - ReachableBlocks reachable; + ReachableBlocks reachable(&f); // Visit all the blocks without doing any extra work. - reachable.visit(&f, [](SILBasicBlock *) { return true; }); + reachable.visit([](SILBasicBlock *) { return true; }); // Remove the blocks we never reached. Assume the entry block is visited. // Reachable's visited set contains dangling pointers during this loop. diff --git a/lib/SILOptimizer/Utils/ValueLifetime.cpp b/lib/SILOptimizer/Utils/ValueLifetime.cpp index 46834b54cf3c8..da8239ac22f8a 100644 --- a/lib/SILOptimizer/Utils/ValueLifetime.cpp +++ b/lib/SILOptimizer/Utils/ValueLifetime.cpp @@ -27,15 +27,14 @@ void ValueLifetimeAnalysis::propagateLiveness() { // Compute the def block only if we have a SILInstruction. If we have a // SILArgument, this will be nullptr. auto *defBB = getDefValueParentBlock(); - SmallVector worklist; int numUsersBeforeDef = 0; // Find the initial set of blocks where the value is live, because // it is used in those blocks. for (SILInstruction *user : userSet) { SILBasicBlock *userBlock = user->getParent(); - if (liveBlocks.insert(userBlock)) - worklist.push_back(userBlock); + if (!inLiveBlocks.testAndSet(userBlock)) + liveBlocks.push_back(userBlock); // A user in the defBB could potentially be located before the defValue. If // we had a SILArgument, defBB will be nullptr, so we should always have @@ -62,8 +61,9 @@ void ValueLifetimeAnalysis::propagateLiveness() { // Now propagate liveness backwards until we hit the block that defines the // value. - while (!worklist.empty()) { - auto *bb = worklist.pop_back_val(); + unsigned workIdx = 0; + while (workIdx < liveBlocks.size()) { + auto *bb = liveBlocks[workIdx++]; // Don't go beyond the definition. if (bb == defBB && !hasUsersBeforeDef) @@ -72,8 +72,8 @@ void ValueLifetimeAnalysis::propagateLiveness() { for (auto *predBB : bb->getPredecessorBlocks()) { // If it's already in the set, then we've already queued and/or // processed the predecessors. - if (liveBlocks.insert(predBB)) - worklist.push_back(predBB); + if (!inLiveBlocks.testAndSet(predBB)) + liveBlocks.push_back(predBB); } } } @@ -186,7 +186,8 @@ bool ValueLifetimeAnalysis::computeFrontier(FrontierImpl &frontier, Mode mode, } } // Handle "exit" edges from the lifetime region. - llvm::SmallPtrSet unhandledFrontierBlocks; + BasicBlockSet unhandledFrontierBlocks(getFunction()); + bool unhandledFrontierBlocksFound = false; for (SILBasicBlock *frontierBB : frontierBlocks) { assert(mode != UsersMustPostDomDef); bool needSplit = false; @@ -201,12 +202,13 @@ bool ValueLifetimeAnalysis::computeFrontier(FrontierImpl &frontier, Mode mode, if (needSplit) { // We need to split the critical edge to create a frontier instruction. unhandledFrontierBlocks.insert(frontierBB); + unhandledFrontierBlocksFound = true; } else { // The first instruction of the exit-block is part of the frontier. frontier.push_back(&*frontierBB->begin()); } } - if (unhandledFrontierBlocks.size() == 0) { + if (!unhandledFrontierBlocksFound) { return true; } @@ -222,7 +224,7 @@ bool ValueLifetimeAnalysis::computeFrontier(FrontierImpl &frontier, Mode mode, succBlocks.push_back(succ); for (unsigned i = 0, e = succBlocks.size(); i != e; ++i) { - if (unhandledFrontierBlocks.count(succBlocks[i])) { + if (unhandledFrontierBlocks.contains(succBlocks[i])) { assert((isCriticalEdge(term, i) || userSet.count(term)) && "actually not a critical edge?"); noCriticalEdges = false; @@ -244,7 +246,7 @@ bool ValueLifetimeAnalysis::computeFrontier(FrontierImpl &frontier, Mode mode, bool ValueLifetimeAnalysis::isWithinLifetime(SILInstruction *inst) { SILBasicBlock *bb = inst->getParent(); // Check if the value is not live anywhere in inst's block. - if (!liveBlocks.count(bb)) + if (!inLiveBlocks.get(bb)) return false; for (const SILSuccessor &succ : bb->getSuccessors()) { // If the value is live at the beginning of any successor block it is also @@ -288,7 +290,7 @@ blockContainsDeallocRef(SILBasicBlock *bb, } bool ValueLifetimeAnalysis::containsDeallocRef(const FrontierImpl &frontier) { - SmallPtrSet frontierBlocks; + BasicBlockSet frontierBlocks(getFunction()); // Search in live blocks where the value is not alive until the end of the // block, i.e. the live range is terminated by a frontier instruction. for (SILInstruction *frontierInst : frontier) { @@ -300,7 +302,7 @@ bool ValueLifetimeAnalysis::containsDeallocRef(const FrontierImpl &frontier) { // Search in all other live blocks where the value is alive until the end of // the block. for (SILBasicBlock *bb : liveBlocks) { - if (frontierBlocks.count(bb) == 0) { + if (frontierBlocks.contains(bb) == 0) { if (blockContainsDeallocRef(bb, defValue, bb->getTerminator())) return true; } diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 0f74d033a77f1..ce7010bd316bd 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -14,6 +14,7 @@ if(SWIFT_INCLUDE_TOOLS) add_subdirectory(IDE) add_subdirectory(Parse) add_subdirectory(Sema) + add_subdirectory(SIL) add_subdirectory(SwiftDemangle) add_subdirectory(Syntax) if(SWIFT_BUILD_SYNTAXPARSERLIB) diff --git a/unittests/SIL/CMakeLists.txt b/unittests/SIL/CMakeLists.txt new file mode 100644 index 0000000000000..620d7bc028d24 --- /dev/null +++ b/unittests/SIL/CMakeLists.txt @@ -0,0 +1,8 @@ +add_swift_unittest(SwiftSILTests + SILBitfieldTest.cpp +) + +target_link_libraries(SwiftSILTests + PRIVATE + swiftSIL +) diff --git a/unittests/SIL/SILBitfieldTest.cpp b/unittests/SIL/SILBitfieldTest.cpp new file mode 100644 index 0000000000000..8fcb265a17e06 --- /dev/null +++ b/unittests/SIL/SILBitfieldTest.cpp @@ -0,0 +1,109 @@ +//===--- SILBitfieldTest.cpp ----------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + + +namespace swift { + +class BasicBlockBitfield; + +// Fake SILFunction and SILBasicBlock. + +struct SILFunction { + BasicBlockBitfield *newestAliveBitfield = nullptr; + uint64_t currentBitfieldID = 1; +}; + +struct SILBasicBlock { + uint32_t customBits = 0; + uint64_t lastInitializedBitfieldID = 0; +}; + +} + +#define SWIFT_SIL_SILFUNCTION_H + +#include "swift/SIL/SILBitfield.h" + +using namespace swift; + +namespace { + +TEST(SILBitfieldTest, Basic) { + SILFunction f; + SILBasicBlock b; + + { + BasicBlockFlag A(&f); + EXPECT_FALSE(A.get(&b)); + + EXPECT_FALSE(A.testAndSet(&b)); + EXPECT_TRUE(A.get(&b)); + + EXPECT_TRUE(A.testAndSet(&b)); + EXPECT_TRUE(A.get(&b)); + + A.reset(&b); + EXPECT_FALSE(A.get(&b)); + + { + BasicBlockBitfield B(&f, 5); + EXPECT_EQ(B.get(&b), 0u); + + B.set(&b, 27); + EXPECT_FALSE(A.get(&b)); + EXPECT_EQ(B.get(&b), 27u); + + A.set(&b); + EXPECT_TRUE(A.get(&b)); + EXPECT_EQ(B.get(&b), 27u); + + B.set(&b, 2); + EXPECT_TRUE(A.get(&b)); + EXPECT_EQ(B.get(&b), 2u); + + B.set(&b, 31); + EXPECT_TRUE(A.get(&b)); + EXPECT_EQ(B.get(&b), 31u); + } + { + BasicBlockBitfield C(&f, 2); + EXPECT_EQ(C.get(&b), 0u); + + BasicBlockFlag D(&f); + EXPECT_FALSE(D.get(&b)); + + BasicBlockBitfield E(&f, 3); + + E.set(&b, 5); + EXPECT_TRUE(A.get(&b)); + EXPECT_EQ(C.get(&b), 0u); + EXPECT_FALSE(D.get(&b)); + EXPECT_EQ(E.get(&b), 5u); + + C.set(&b, 1); + EXPECT_TRUE(A.get(&b)); + EXPECT_EQ(C.get(&b), 1u); + EXPECT_FALSE(D.get(&b)); + EXPECT_EQ(E.get(&b), 5u); + } + } + { + BasicBlockBitfield F(&f, 32); + EXPECT_EQ(F.get(&b), 0u); + F.set(&b, 0xdeadbeaf); + EXPECT_EQ(F.get(&b), 0xdeadbeaf); + } +} + +}