From 2ca9a3b9ce54acf8216c48044f1bb7ce91c21dbe Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 2 Dec 2022 14:36:42 +0100 Subject: [PATCH 1/8] SIL: fix APIs for (re)moving instructions Instructions can only be moved and erased, but never _removed_ from a block. --- include/swift/SIL/SILBasicBlock.h | 3 ++- include/swift/SIL/SILInstruction.h | 3 --- lib/SIL/IR/SILBasicBlock.cpp | 19 +++++++++++++++---- lib/SIL/IR/SILInstruction.cpp | 19 ++----------------- .../Mandatory/AddressLowering.cpp | 6 +++--- 5 files changed, 22 insertions(+), 28 deletions(-) diff --git a/include/swift/SIL/SILBasicBlock.h b/include/swift/SIL/SILBasicBlock.h index cd759d47c59..e4da0d5a85e 100644 --- a/include/swift/SIL/SILBasicBlock.h +++ b/include/swift/SIL/SILBasicBlock.h @@ -148,9 +148,10 @@ public: void push_back(SILInstruction *I); void push_front(SILInstruction *I); - void remove(SILInstruction *I); void erase(SILInstruction *I); void erase(SILInstruction *I, SILModule &module); + static void moveInstruction(SILInstruction *inst, SILInstruction *beforeInst); + void moveInstructionToFront(SILInstruction *inst); void eraseAllInstructions(SILModule &module); diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 130f664c58b..1b07bfb170e 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -376,9 +376,6 @@ protected: NumCreatedInstructions++; } - /// This method unlinks 'self' from the containing basic block. - void removeFromParent(); - ~SILInstruction() { NumDeletedInstructions++; } diff --git a/lib/SIL/IR/SILBasicBlock.cpp b/lib/SIL/IR/SILBasicBlock.cpp index dcf4a2087f8..41661565002 100644 --- a/lib/SIL/IR/SILBasicBlock.cpp +++ b/lib/SIL/IR/SILBasicBlock.cpp @@ -91,10 +91,6 @@ void SILBasicBlock::push_front(SILInstruction *I) { InstList.push_front(I); } -void SILBasicBlock::remove(SILInstruction *I) { - InstList.remove(I); -} - void SILBasicBlock::eraseAllInstructions(SILModule &module) { while (!empty()) { erase(&*begin(), module); @@ -112,6 +108,21 @@ void SILBasicBlock::erase(SILInstruction *I, SILModule &module) { module.scheduleForDeletion(I); } +void SILBasicBlock::moveInstruction(SILInstruction *inst, + SILInstruction *beforeInst) { + if (inst == beforeInst) + return; + inst->getParent()->InstList.remove(inst); + inst->ParentBB = nullptr; + beforeInst->getParent()->insert(beforeInst->getIterator(), inst); +} + +void SILBasicBlock::moveInstructionToFront(SILInstruction *inst) { + inst->getParent()->InstList.remove(inst); + inst->ParentBB = nullptr; + push_front(inst); +} + /// This method unlinks 'self' from the containing SILFunction and deletes it. void SILBasicBlock::eraseFromParent() { getParent()->eraseBlock(this); diff --git a/lib/SIL/IR/SILInstruction.cpp b/lib/SIL/IR/SILInstruction.cpp index 5d8c570149b..3243351dc78 100644 --- a/lib/SIL/IR/SILInstruction.cpp +++ b/lib/SIL/IR/SILInstruction.cpp @@ -103,16 +103,6 @@ transferNodesFromList(llvm::ilist_traits &L2, ASSERT_IMPLEMENTS_STATIC(CLASS, PARENT, classof, bool(SILNodePointer)); #include "swift/SIL/SILNodes.def" -void SILInstruction::removeFromParent() { -#ifndef NDEBUG - for (auto result : getResults()) { - assert(result->use_empty() && "Uses of SILInstruction remain at deletion."); - } -#endif - getParent()->remove(this); - ParentBB = nullptr; -} - /// eraseFromParent - This method unlinks 'self' from the containing basic /// block and deletes it. /// @@ -126,18 +116,13 @@ void SILInstruction::eraseFromParent() { } void SILInstruction::moveFront(SILBasicBlock *Block) { - getParent()->remove(this); - Block->push_front(this); + Block->moveInstructionToFront(this); } /// Unlink this instruction from its current basic block and insert it into /// the basic block that Later lives in, right before Later. void SILInstruction::moveBefore(SILInstruction *Later) { - if (this == Later) - return; - - getParent()->remove(this); - Later->getParent()->insert(Later, this); + SILBasicBlock::moveInstruction(this, Later); } /// Unlink this instruction from its current basic block and insert it into diff --git a/lib/SILOptimizer/Mandatory/AddressLowering.cpp b/lib/SILOptimizer/Mandatory/AddressLowering.cpp index 621dbb56e80..f008f8a8c50 100644 --- a/lib/SILOptimizer/Mandatory/AddressLowering.cpp +++ b/lib/SILOptimizer/Mandatory/AddressLowering.cpp @@ -3895,9 +3895,9 @@ static void deleteRewrittenInstructions(AddressLoweringState &pass) { continue; } // willDeleteInstruction was already called for open_existential_value to - // update the registered type. Carry out the remaining deletion steps. - deadInst->getParent()->remove(deadInst); - pass.getModule()->scheduleForDeletion(deadInst); + // update the registered type. Now fully erase the instruction, which will + // harmlessly call willDeleteInstruction again. + deadInst->getParent()->erase(deadInst); } pass.valueStorageMap.clear(); From 3b94b6042091ca0561bd3f72dde43bef818df13f Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 2 Dec 2022 14:46:28 +0100 Subject: [PATCH 2/8] SIL: support calling `SILModule::willDeleteInstruction` twice for the same instruction. This happens in AddressLowering. --- lib/SIL/IR/SILModule.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 1a61bd6e043..6e19999f64a 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -264,9 +264,11 @@ void SILModule::willDeleteInstruction(SILInstruction *I) { if (const CanOpenedArchetypeType archeTy = svi->getDefinedOpenedArchetype()) { OpenedArchetypeKey key = {archeTy, svi->getFunction()}; - assert(RootOpenedArchetypeDefs.lookup(key) == svi && - "archetype def was not registered"); - RootOpenedArchetypeDefs.erase(key); + // In case `willDeleteInstruction` is called twice for the same instruction, + // we need to check if the archetype is really still in the map for this + // instruction. + if (RootOpenedArchetypeDefs.lookup(key) == svi) + RootOpenedArchetypeDefs.erase(key); } } } From d8c2e8ae77d95b0dcb08b30066816e166fc8d95c Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 2 Dec 2022 18:58:06 +0100 Subject: [PATCH 3/8] SIL: change SILModule's `scheduledForDeletion` from a double linked list to a `std::vector` --- include/swift/SIL/SILModule.h | 2 +- lib/SIL/IR/SILModule.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index 60054d57a2d..9f097342fea 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -217,7 +217,7 @@ private: /// This avoids dangling instruction pointers within the run of a pass and in /// analysis caches. Note that the analysis invalidation mechanism ensures /// that analysis caches are invalidated before flushDeletedInsts(). - llvm::iplist scheduledForDeletion; + std::vector scheduledForDeletion; /// The swift Module associated with this SILModule. ModuleDecl *TheSwiftModule; diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 6e19999f64a..3a1f301bb69 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -164,8 +164,8 @@ void SILModule::checkForLeaks() const { if (!getOptions().checkSILModuleLeaks) return; - int instsInModule = std::distance(scheduledForDeletion.begin(), - scheduledForDeletion.end()); + int instsInModule = scheduledForDeletion.size(); + for (const SILFunction &F : *this) { const SILFunction *sn = &F; do { @@ -280,11 +280,11 @@ void SILModule::scheduleForDeletion(SILInstruction *I) { } void SILModule::flushDeletedInsts() { - while (!scheduledForDeletion.empty()) { - SILInstruction *inst = &*scheduledForDeletion.begin(); - scheduledForDeletion.erase(inst); - AlignedFree(inst); + for (SILInstruction *instToDelete : scheduledForDeletion) { + SILInstruction::destroy(instToDelete); + AlignedFree(instToDelete); } + scheduledForDeletion.clear(); } SILWitnessTable * From 05a63c70c5acb55872ce82cd6cffae552a420a91 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 2 Dec 2022 22:01:19 +0100 Subject: [PATCH 4/8] SIL: change way how a SILInstruction is marked as deleted Instead of setting the parent pointer to null, set the `lastInitializedBitfieldID` to -1. This allows to keep the parent block information, even when an instruction is removed from it's list. --- include/swift/SIL/SILBasicBlock.h | 2 +- include/swift/SIL/SILBitfield.h | 2 +- include/swift/SIL/SILFunction.h | 2 +- include/swift/SIL/SILInstruction.h | 3 +-- include/swift/SIL/SILNode.h | 13 ++++++++++++- lib/SIL/IR/SILBasicBlock.cpp | 4 ++-- lib/SIL/IR/SILInstruction.cpp | 6 ------ lib/SIL/IR/SILModule.cpp | 1 - unittests/SIL/SILBitfieldTest.cpp | 4 ++-- 9 files changed, 20 insertions(+), 17 deletions(-) diff --git a/include/swift/SIL/SILBasicBlock.h b/include/swift/SIL/SILBasicBlock.h index e4da0d5a85e..75d11841a78 100644 --- a/include/swift/SIL/SILBasicBlock.h +++ b/include/swift/SIL/SILBasicBlock.h @@ -85,7 +85,7 @@ private: /// DD and EEE are uninitialized /// /// See also: SILBitfield::bitfieldID, SILFunction::currentBitfieldID. - uint64_t lastInitializedBitfieldID = 0; + int64_t lastInitializedBitfieldID = 0; // Used by `BasicBlockBitfield`. unsigned getCustomBits() const { return customBits; } diff --git a/include/swift/SIL/SILBitfield.h b/include/swift/SIL/SILBitfield.h index c559b0802b1..5d9f1e7f00a 100644 --- a/include/swift/SIL/SILBitfield.h +++ b/include/swift/SIL/SILBitfield.h @@ -30,7 +30,7 @@ template class SILBitfield { /// that the bits of that block are not initialized yet. /// See also: SILBasicBlock::lastInitializedBitfieldID, /// SILFunction::currentBitfieldID - uint64_t bitfieldID; + int64_t bitfieldID; short startBit; short endBit; diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 49e5bf930d7..8d4e880c342 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -266,7 +266,7 @@ private: /// A monotonically increasing ID which is incremented whenever a /// BasicBlockBitfield or NodeBitfield is constructed. /// For details see SILBitfield::bitfieldID; - uint64_t currentBitfieldID = 1; + int64_t currentBitfieldID = 1; /// Unique identifier for vector indexing and deterministic sorting. /// May be reused when zombie functions are recovered. diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 1b07bfb170e..3ee91f16c23 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -391,7 +391,7 @@ public: /// Returns true if this instruction is removed from its function and /// scheduled to be deleted. - bool isDeleted() const { return !ParentBB; } + bool isDeleted() const { return asSILNode()->isMarkedAsDeleted(); } enum class MemoryBehavior { None, @@ -10138,7 +10138,6 @@ public: } void addNodeToList(SILInstruction *I); - void removeNodeFromList(SILInstruction *I); void transferNodesFromList(ilist_traits &L2, instr_iterator first, instr_iterator last); diff --git a/include/swift/SIL/SILNode.h b/include/swift/SIL/SILNode.h index c1865add4d1..e4ccfbaa8b0 100644 --- a/include/swift/SIL/SILNode.h +++ b/include/swift/SIL/SILNode.h @@ -330,8 +330,13 @@ protected: /// -> AAA, BB and C are initialized, /// DD and EEE are uninitialized /// + /// If the ID is negative, it means that the node (in case it's an instruction) + /// is deleted, i.e. it does not belong to the function anymore. Conceptually + /// this results in setting all bitfields to zero, which e.g. "removes" the + /// node from all NodeSets. + /// /// See also: SILBitfield::bitfieldID, SILFunction::currentBitfieldID. - uint64_t lastInitializedBitfieldID = 0; + int64_t lastInitializedBitfieldID = 0; private: SwiftMetatype getSILNodeMetatype(SILNodeKind kind); @@ -389,6 +394,12 @@ public: lastInitializedBitfieldID = 0; } + void markAsDeleted() { + lastInitializedBitfieldID = -1; + } + + bool isMarkedAsDeleted() const { return lastInitializedBitfieldID < 0; } + static SILNode *instAsNode(SILInstruction *inst); static const SILNode *instAsNode(const SILInstruction *inst); diff --git a/lib/SIL/IR/SILBasicBlock.cpp b/lib/SIL/IR/SILBasicBlock.cpp index 41661565002..b1116a55920 100644 --- a/lib/SIL/IR/SILBasicBlock.cpp +++ b/lib/SIL/IR/SILBasicBlock.cpp @@ -103,8 +103,10 @@ void SILBasicBlock::erase(SILInstruction *I) { } void SILBasicBlock::erase(SILInstruction *I, SILModule &module) { + assert(!I->isDeleted() && "double delete of instruction"); module.willDeleteInstruction(I); InstList.remove(I); + I->asSILNode()->markAsDeleted(); module.scheduleForDeletion(I); } @@ -113,13 +115,11 @@ void SILBasicBlock::moveInstruction(SILInstruction *inst, if (inst == beforeInst) return; inst->getParent()->InstList.remove(inst); - inst->ParentBB = nullptr; beforeInst->getParent()->insert(beforeInst->getIterator(), inst); } void SILBasicBlock::moveInstructionToFront(SILInstruction *inst) { inst->getParent()->InstList.remove(inst); - inst->ParentBB = nullptr; push_front(inst); } diff --git a/lib/SIL/IR/SILInstruction.cpp b/lib/SIL/IR/SILInstruction.cpp index 3243351dc78..5521e2b9ca2 100644 --- a/lib/SIL/IR/SILInstruction.cpp +++ b/lib/SIL/IR/SILInstruction.cpp @@ -61,15 +61,9 @@ SILBasicBlock *llvm::ilist_traits::getContainingBlock() { void llvm::ilist_traits::addNodeToList(SILInstruction *I) { - assert(I->ParentBB == nullptr && "Already in a list!"); I->ParentBB = getContainingBlock(); } -void llvm::ilist_traits::removeNodeFromList(SILInstruction *I) { - // When an instruction is removed from a BB, clear the parent pointer. - I->ParentBB = nullptr; -} - void llvm::ilist_traits:: transferNodesFromList(llvm::ilist_traits &L2, instr_iterator first, instr_iterator last) { diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 3a1f301bb69..cb20a9c0774 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -276,7 +276,6 @@ void SILModule::willDeleteInstruction(SILInstruction *I) { void SILModule::scheduleForDeletion(SILInstruction *I) { I->dropAllReferences(); scheduledForDeletion.push_back(I); - I->ParentBB = nullptr; } void SILModule::flushDeletedInsts() { diff --git a/unittests/SIL/SILBitfieldTest.cpp b/unittests/SIL/SILBitfieldTest.cpp index 0614d0f8c50..10e49934c3e 100644 --- a/unittests/SIL/SILBitfieldTest.cpp +++ b/unittests/SIL/SILBitfieldTest.cpp @@ -21,13 +21,13 @@ class BasicBlockBitfield; struct SILFunction { BasicBlockBitfield *newestAliveBlockBitfield = nullptr; - uint64_t currentBitfieldID = 1; + int64_t currentBitfieldID = 1; }; struct SILBasicBlock { SILFunction *function; uint32_t customBits = 0; - uint64_t lastInitializedBitfieldID = 0; + int64_t lastInitializedBitfieldID = 0; enum { numCustomBits = 32 }; From c180d1363e5fa5d20bd3f497eb250c73c8b9cbb0 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 2 Dec 2022 15:21:23 +0100 Subject: [PATCH 5/8] SIL: simplify deleting instruction while iterating over instructions. Add `deletableInstructions()` and `reverseDeletableInstructions()` in SILBasicBlock. It allows deleting instructions while iterating over all instructions of the block. This is a replacement for `InstructionDeleter::updatingRange()`. It's a simpler implementation than the existing `UpdatingListIterator` and `UpdatingInstructionIteratorRegistry`, because it just needs to keep the prev/next pointers for "deleted" instructions instead of the iterator-registration machinery. It's also safer, because it doesn't require to delete instructions via a specific instance of an InstructionDeleter (which can be missed easily). --- include/swift/SIL/SILBasicBlock.h | 59 ++++ include/swift/SIL/SILInstruction.h | 52 ++++ .../SILOptimizer/Utils/InstructionDeleter.h | 26 +- .../Utils/UpdatingInstructionIterator.h | 293 ------------------ .../IRGenTransforms/TargetConstantFolding.cpp | 6 +- .../Mandatory/ClosureLifetimeFixup.cpp | 6 +- .../Mandatory/PredictableMemOpt.cpp | 8 +- .../Transforms/DeadObjectElimination.cpp | 12 +- .../Transforms/GenericSpecializer.cpp | 4 +- lib/SILOptimizer/Transforms/SILMem2Reg.cpp | 4 +- .../UtilityPasses/OSSALifetimeAnalysis.cpp | 4 +- .../UtilityPasses/ParseTestSpecification.cpp | 8 +- 12 files changed, 143 insertions(+), 339 deletions(-) delete mode 100644 include/swift/SILOptimizer/Utils/UpdatingInstructionIterator.h diff --git a/include/swift/SIL/SILBasicBlock.h b/include/swift/SIL/SILBasicBlock.h index 75d11841a78..ab3b95712e8 100644 --- a/include/swift/SIL/SILBasicBlock.h +++ b/include/swift/SIL/SILBasicBlock.h @@ -31,6 +31,51 @@ class SILFunction; class SILArgument; class SILPrintContext; +/// Instruction iterator which allows to "delete" instructions while iterating +/// over the instruction list. +/// +/// Iteration with this iterator allows to delete the current, the next or any +/// instruction in the list while iterating. +/// This works because instruction deletion is deferred (for details see +/// `SILModule::scheduledForDeletion`) and removing an instruction from the list +/// keeps the prev/next pointers (see `SILInstructionListBase`). +template +class DeletableInstructionsIterator { + using Self = DeletableInstructionsIterator; + + IteratorBase base; + IteratorBase end; + +public: + using value_type = typename IteratorBase::value_type; + using difference_type = ptrdiff_t; + using pointer = value_type *; + using iterator_category = std::forward_iterator_tag; + + DeletableInstructionsIterator(IteratorBase base, IteratorBase end) + : base(base), end(end) {} + + value_type &operator*() const { return *base; } + + SILInstruction *operator->() const { return base.operator->(); } + + Self &operator++() { + // If the current instruction is "deleted" (which means: removed from the + // list), it's prev/next pointers still point to the next instruction which + // is still in the list - or "deleted", too. + ++base; + // Skip over all deleted instructions. Eventually we reach an instruction + // is still in the list (= not "deleted") or the end iterator. + while (base != end && base->isDeleted()) { + ++base; + } + return *this; + } + + bool operator==(const Self &rhs) const { return base == rhs.base; } + bool operator!=(const Self &rhs) const { return !(*this == rhs); } +}; + class SILBasicBlock : public llvm::ilist_node, public SILAllocated, public SwiftObjectHeader { @@ -184,6 +229,20 @@ public: const_reverse_iterator rbegin() const { return InstList.rbegin(); } const_reverse_iterator rend() const { return InstList.rend(); } + /// Allows deleting instructions while iterating over all instructions of the + /// block. + /// + /// For details see `DeletableInstructionsIterator`. + llvm::iterator_range> + deletableInstructions() { return {{begin(), end()}, {end(), end()}}; } + + /// Allows deleting instructions while iterating over all instructions of the + /// block in reverse order. + /// + /// For details see `DeletableInstructionsIterator`. + llvm::iterator_range> + reverseDeletableInstructions() { return {{rbegin(), rend()}, {rend(), rend()}}; } + TermInst *getTerminator() { assert(!InstList.empty() && "Can't get successors for malformed block"); return cast(&InstList.back()); diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 3ee91f16c23..0e602f155ec 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -49,6 +49,58 @@ #include "llvm/Support/TrailingObjects.h" #include +namespace llvm { +namespace ilist_detail { + +/// The base class of the instruction list in SILBasicBlock. +/// +/// We need a custom base class to not clear the prev/next pointers when +/// removing an instruction from the list. +class SILInstructionListBase : public ilist_base { +public: + /// Remove an instruction from the list. + /// + /// In contrast to the default implementation, it does not clear the prev/ + /// next pointers in the node. This is needed to being able to remove + /// instructions from the list while iterating over the list. + /// For details see `DeletableInstructionsIterator`. + template static void remove(T &N) { + node_base_type *Prev = N.getPrev(); + node_base_type *Next = N.getNext(); + Next->setPrev(Prev); + Prev->setNext(Next); + } + + template static void insertBefore(T &Next, T &N) { + insertBeforeImpl(Next, N); + } + + template static void transferBefore(T &Next, T &First, T &Last) { + transferBeforeImpl(Next, First, Last); + } +}; + +// This template specialization is needed to replace the default instruction +// list base class with `SILInstructionListBase`. +template <> struct compute_node_options<::swift::SILInstruction> { + struct type { + typedef ::swift::SILInstruction value_type; + typedef value_type *pointer; + typedef value_type &reference; + typedef const value_type *const_pointer; + typedef const value_type &const_reference; + + static const bool enable_sentinel_tracking = false; + static const bool is_sentinel_tracking_explicit = false; + typedef void tag; + typedef ilist_node_base node_base_type; + typedef SILInstructionListBase list_base_type; + }; +}; + +} // end namespace ilist_detail +} // end llvm namespace + namespace swift { class AllocationInst; diff --git a/include/swift/SILOptimizer/Utils/InstructionDeleter.h b/include/swift/SILOptimizer/Utils/InstructionDeleter.h index dd39fa7b21a..12fb0b61858 100644 --- a/include/swift/SILOptimizer/Utils/InstructionDeleter.h +++ b/include/swift/SILOptimizer/Utils/InstructionDeleter.h @@ -97,7 +97,6 @@ #include "swift/SIL/SILInstruction.h" #include "swift/SILOptimizer/Utils/InstModCallbacks.h" -#include "swift/SILOptimizer/Utils/UpdatingInstructionIterator.h" namespace swift { @@ -116,29 +115,16 @@ class InstructionDeleter { /// instruction's operands. This has to be deterministic. SmallSetVector deadInstructions; - UpdatingInstructionIteratorRegistry iteratorRegistry; + /// Callbacks used when adding/deleting instructions. + InstModCallbacks callbacks; public: - InstructionDeleter() : deadInstructions(), iteratorRegistry() {} + InstructionDeleter() : deadInstructions() {} - InstructionDeleter(InstModCallbacks &&chainedCallbacks) - : deadInstructions(), iteratorRegistry(std::move(chainedCallbacks)) {} + InstructionDeleter(InstModCallbacks &&callbacks) + : deadInstructions(), callbacks(std::move(callbacks)) {} - UpdatingInstructionIteratorRegistry &getIteratorRegistry() { - return iteratorRegistry; - } - - InstModCallbacks &getCallbacks() { return iteratorRegistry.getCallbacks(); } - - llvm::iterator_range - updatingRange(SILBasicBlock *bb) { - return iteratorRegistry.makeIteratorRange(bb); - } - - llvm::iterator_range - updatingReverseRange(SILBasicBlock *bb) { - return iteratorRegistry.makeReverseIteratorRange(bb); - } + InstModCallbacks &getCallbacks() { return callbacks; } bool hadCallbackInvocation() const { return const_cast(this) diff --git a/include/swift/SILOptimizer/Utils/UpdatingInstructionIterator.h b/include/swift/SILOptimizer/Utils/UpdatingInstructionIterator.h deleted file mode 100644 index c605e38722a..00000000000 --- a/include/swift/SILOptimizer/Utils/UpdatingInstructionIterator.h +++ /dev/null @@ -1,293 +0,0 @@ -//===--- UpdatingInstructionIterator.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 -// -//===----------------------------------------------------------------------===// -/// -/// Classes for tracking instruction iterators to be updated when instructions -/// are added or deleted. -/// -/// UpdatingInstructionIteratorRegistry typically resides in the currently -/// active InstructionDeleter object. -/// -/// UpdatingListIterator is the iterator adapter. -/// It is produced by: UpdatingInstructionIteratorRegistry::makeIterator() -/// -/// Iterators are typically encapsulated in a range returned by -/// UpdatingInstructionIteratorRegistry::iteratorRange() for use in -/// range-based for loops: -/// -/// for (SILInstruction *inst : registry.iteratorRange(bb)) ... -/// -/// Or more commonly, directly from the InstructionDeleter: -/// -/// for (SILInstruction *inst : deleter.updatingRange(bb)) ... -/// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_SILOPTIMIZER_UTILS_UPDATINGINSTRUCTIONITERATOR_H -#define SWIFT_SILOPTIMIZER_UTILS_UPDATINGINSTRUCTIONITERATOR_H - -#include "swift/SIL/SILBasicBlock.h" -#include "swift/SIL/SILInstruction.h" - -namespace swift { - -/// Safely iterate over list elements while deleting elements and inserting new -/// elements. Advancing the iterator moves to the element following most -/// recently visited element. This holds even if the most recent element has -/// been deleted and new instructions have been added after its original -/// position. -/// -/// Iterator copies are expensive because each copy registers -/// itself in a central data structure. Postorder increment is currently -/// unavailable to avoid excessive copies. -/// -/// This adapter converts the base iterator's value_type into a pointer to the -/// same type. When the element has been deleted, dereferencing the iterator -/// returns a nullptr. This works with any bidirectional base iterator in which -/// the storage of each element is stable. Typically an llvm::ilist. The -/// implementation assumes that both forward and reverse iterators point to the -/// storage of the current element. -template -class UpdatingListIterator { - using Self = UpdatingListIterator; - - Registry *registry; - IteratorBase base; - bool isDeleted = false; - -public: - using value_type = typename IteratorBase::pointer; - using difference_type = ptrdiff_t; - using pointer = value_type *; - using iterator_category = std::bidirectional_iterator_tag; - - UpdatingListIterator(Registry ®istry, IteratorBase base) - : registry(®istry), base(base) { - registry.registerIterator(this); - } - - ~UpdatingListIterator() { - if (registry) - registry->destroyIterator(this); - } - - // Copying forces a new entry in the registry. - UpdatingListIterator(const Self &rhs) - : registry(rhs.registry), base(rhs.base), isDeleted(rhs.isDeleted) { - registry->registerIterator(this); - } - - Self &operator=(const Self &rhs) { - this->registry = rhs.registry; - this->base = rhs.base; - this->advanced = rhs.advanced; - registry->registerIterator(this); - return *this; - } - - /// Explicit conversion between forward/reverse iterators. - /// - /// Note that this does not produce the same result as getting the iterator's - /// instruction and producing a new iterator in the opposite - /// direction. Instead, the new iterator points to the previous instruction in - /// the original iteration order. This ensures that forward and reverse ranges - /// enclose the same set of instructions. - template - explicit UpdatingListIterator( - const UpdatingListIterator &rhs) - : UpdatingListIterator(IteratorBase(rhs.base)) {} - - /// This returns nullptr for deleted instructions. - value_type operator*() const { return isDeleted ? nullptr : &*base; } - - SILInstruction *operator->() const { return operator*(); } - - Self &operator++() { - advance(); - return *this; - } - - bool operator==(const Self &rhs) const { - return this->registry == rhs.registry && this->base == rhs.base - && this->isDeleted == rhs.isDeleted; - } - bool operator!=(const Self &rhs) const { return !(*this == rhs); } - - void advance() { - if (isDeleted) - isDeleted = false; - else - ++base; - } - - void notifyDelete(IteratorBase positionToDelete) { - if (base == positionToDelete) { - isDeleted = true; - ++base; - } - } - - void notifyNew(IteratorBase newPosition) { - if (isDeleted && std::prev(base) == newPosition) { - // The deleted forward iterator was already advanced. Move it back to the - // position of the new element. - --base; - } - } -}; - -class UpdatingInstructionIteratorRegistry; - -using UpdatingInstructionIterator = - UpdatingListIterator; -using UpdatingReverseInstructionIterator = - UpdatingListIterator; - -/// Track instruction iterators that need updating when instructions are added or -/// deleted. Iterators can be tracked across multiple levels of the call -/// stack. This registry object must outlive any iterators that it vends. -/// -/// While the registry is active, all instruction modification must go through -/// its callbacks. -class UpdatingInstructionIteratorRegistry { - - /// Track iterators that need updating. Seldom expect to have more than 4 - /// (making a single range creates 4 but immediately discards 2). It is - /// possible for iterators to be copied and destroyed on each iteration of a - /// loop (although we should try hard to avoid that), so this does need to - /// immediately reuse old slots. - SmallVector forwardIterators; - SmallVector reverseIterators; - - std::function chainedDelete; - std::function chainedNew; - - /// Callbacks used when adding/deleting instructions. - InstModCallbacks callbacks; - - -public: - UpdatingInstructionIteratorRegistry() { - callbacks = std::move(InstModCallbacks() - .onDelete([this](SILInstruction *toDelete) { - notifyDelete(toDelete); - toDelete->eraseFromParent(); - }) - .onCreateNewInst( - [this](SILInstruction *newlyCreatedInst) { - notifyNew(newlyCreatedInst); - })); - } - - UpdatingInstructionIteratorRegistry(InstModCallbacks &&chainedCallbacks) : - // Copy the two std::functions that we need. The rest of the callbacks are - // copied implicitly by assignment. - chainedDelete(std::move(chainedCallbacks.deleteInstFunc)), - chainedNew(std::move(chainedCallbacks.createdNewInstFunc)) { - callbacks = std::move(chainedCallbacks - .onDelete([this](SILInstruction *toDelete) { - notifyDelete(toDelete); - if (chainedDelete) { - chainedDelete(toDelete); - return; - } - toDelete->eraseFromParent(); - }) - .onCreateNewInst( - [this](SILInstruction *newlyCreatedInst) { - notifyNew(newlyCreatedInst); - if (chainedNew) { - chainedNew(newlyCreatedInst); - } - })); - } - - // The callbacks capture 'this'. So copying is invalid. - UpdatingInstructionIteratorRegistry( - const UpdatingInstructionIteratorRegistry &) = delete; - - UpdatingInstructionIteratorRegistry & - operator=(const UpdatingInstructionIteratorRegistry &) = delete; - - InstModCallbacks &getCallbacks() { return callbacks; } - - void registerIterator(UpdatingInstructionIterator *i) { - forwardIterators.push_back(i); - } - - void registerIterator(UpdatingReverseInstructionIterator *i) { - reverseIterators.push_back(i); - } - - void destroyIterator(UpdatingInstructionIterator *i) { - auto pos = std::find(forwardIterators.begin(), forwardIterators.end(), i); - assert(pos != forwardIterators.end() && "unregistered iterator"); - forwardIterators.erase(pos); - } - void destroyIterator(UpdatingReverseInstructionIterator *i) { - auto pos = std::find(reverseIterators.begin(), reverseIterators.end(), i); - assert(pos != reverseIterators.end() && "unregistered iterator"); - reverseIterators.erase(pos); - } - - UpdatingInstructionIterator makeIterator(SILBasicBlock::iterator i) { - return {*this, i}; - } - - UpdatingInstructionIterator makeIterator(SILInstruction *inst) { - return makeIterator(inst->getIterator()); - } - - UpdatingReverseInstructionIterator - makeReverseIterator(SILBasicBlock::reverse_iterator i) { - return {*this, i}; - } - - UpdatingReverseInstructionIterator - makeReverseIterator(SILInstruction *inst) { - return makeReverseIterator(inst->getReverseIterator()); - } - - llvm::iterator_range - makeIteratorRange(SILBasicBlock *bb) { - return {makeIterator(bb->begin()), makeIterator(bb->end())}; - } - - llvm::iterator_range - makeReverseIteratorRange(SILBasicBlock *bb) { - return {makeReverseIterator(bb->rbegin()), makeReverseIterator(bb->rend())}; - } - -protected: - void notifyDelete(SILInstruction *toDelete) { - for (auto *iterator : forwardIterators) { - iterator->notifyDelete(toDelete->getIterator()); - } - for (auto *iterator : reverseIterators) { - iterator->notifyDelete(toDelete->getReverseIterator()); - } - } - - void notifyNew(SILInstruction *newlyCreatedInst) { - for (auto *iterator : forwardIterators) { - iterator->notifyNew(newlyCreatedInst->getIterator()); - } - for (auto *iterator : reverseIterators) { - iterator->notifyNew(newlyCreatedInst->getReverseIterator()); - } - } -}; - -} // end namespace swift - -#endif diff --git a/lib/SILOptimizer/IRGenTransforms/TargetConstantFolding.cpp b/lib/SILOptimizer/IRGenTransforms/TargetConstantFolding.cpp index be4d74a4cab..aebfe777ce1 100644 --- a/lib/SILOptimizer/IRGenTransforms/TargetConstantFolding.cpp +++ b/lib/SILOptimizer/IRGenTransforms/TargetConstantFolding.cpp @@ -72,9 +72,9 @@ private: for (SILBasicBlock &block : function) { InstructionDeleter deleter; - for (SILInstruction *inst : deleter.updatingRange(&block)) { - if (constFold(inst, IGM)) { - deleter.forceDelete(inst); + for (SILInstruction &inst : block.deletableInstructions()) { + if (constFold(&inst, IGM)) { + deleter.forceDelete(&inst); changed = true; } } diff --git a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp index a94236cb620..c42c7b5bad1 100644 --- a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp +++ b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp @@ -1004,9 +1004,9 @@ static bool fixupClosureLifetimes(SILFunction &fn, bool &checkStackNesting, for (auto &block : fn) { SILSSAUpdater updater; - for (SILInstruction *inst : updater.getDeleter().updatingRange(&block)) { + for (SILInstruction &inst : block.deletableInstructions()) { // Handle, copy_block_without_escaping instructions. - if (auto *cb = dyn_cast(inst)) { + if (auto *cb = dyn_cast(&inst)) { if (fixupCopyBlockWithoutEscaping(cb, updater.getDeleter(), modifiedCFG)) { changed = true; } @@ -1015,7 +1015,7 @@ static bool fixupClosureLifetimes(SILFunction &fn, bool &checkStackNesting, // Otherwise, look at convert_escape_to_noescape [not_guaranteed] // instructions. - auto *cvt = dyn_cast(inst); + auto *cvt = dyn_cast(&inst); if (!cvt || cvt->isLifetimeGuaranteed()) continue; diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index a10adeeb0b0..ecd815545fd 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -2856,9 +2856,9 @@ bool swift::optimizeMemoryAccesses(SILFunction &fn) { InstructionDeleter deleter; for (auto &bb : fn) { - for (SILInstruction *inst : deleter.updatingRange(&bb)) { + for (SILInstruction &inst : bb.deletableInstructions()) { // First see if i is an allocation that we can optimize. If not, skip it. - AllocationInst *alloc = getOptimizableAllocation(inst); + AllocationInst *alloc = getOptimizableAllocation(&inst); if (!alloc) { continue; } @@ -2900,9 +2900,9 @@ bool swift::eliminateDeadAllocations(SILFunction &fn) { for (auto &bb : fn) { InstructionDeleter deleter; - for (SILInstruction *inst : deleter.updatingRange(&bb)) { + for (SILInstruction &inst : bb.deletableInstructions()) { // First see if i is an allocation that we can optimize. If not, skip it. - AllocationInst *alloc = getOptimizableAllocation(inst); + AllocationInst *alloc = getOptimizableAllocation(&inst); if (!alloc) { continue; } diff --git a/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp b/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp index f1a57362b75..da4c40fc827 100644 --- a/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp @@ -752,16 +752,16 @@ class DeadObjectElimination : public SILFunctionTransform { for (auto &BB : Fn) { - for (SILInstruction *inst : deleter.updatingRange(&BB)) { - if (auto *A = dyn_cast(inst)) + for (SILInstruction &inst : BB.deletableInstructions()) { + if (auto *A = dyn_cast(&inst)) Changed |= processAllocRef(A); - else if (auto *A = dyn_cast(inst)) + else if (auto *A = dyn_cast(&inst)) Changed |= processAllocStack(A); - else if (auto *KPI = dyn_cast(inst)) + else if (auto *KPI = dyn_cast(&inst)) Changed |= processKeyPath(KPI); - else if (auto *A = dyn_cast(inst)) + else if (auto *A = dyn_cast(&inst)) Changed |= processAllocBox(A); - else if (auto *A = dyn_cast(inst)) + else if (auto *A = dyn_cast(&inst)) Changed |= processAllocApply(A, DEBlocks); } deleter.cleanupDeadInstructions(); diff --git a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp index c9f2deb5c94..6ad2ae82318 100644 --- a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp +++ b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp @@ -291,8 +291,8 @@ bool MandatoryGenericSpecializer::optimize(SILFunction *func, if (!rrBlocks.reachesReturn(&block) || !neBlocks.isNonErrorHandling(&block)) continue; - for (SILInstruction *inst : deleter.updatingReverseRange(&block)) { - changed |= optimizeInst(inst, funcBuilder, deleter, cha, invalidatedStackNesting); + for (SILInstruction &inst : block.reverseDeletableInstructions()) { + changed |= optimizeInst(&inst, funcBuilder, deleter, cha, invalidatedStackNesting); } } deleter.cleanupDeadInstructions(); diff --git a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp index bcec6e92e71..00249ac9c22 100644 --- a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp +++ b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp @@ -1994,8 +1994,8 @@ bool MemoryToRegisters::run() { if (!domInfo->isReachableFromEntry(&block)) { continue; } - for (SILInstruction *inst : deleter.updatingReverseRange(&block)) { - auto *asi = dyn_cast(inst); + for (SILInstruction &inst : block.reverseDeletableInstructions()) { + auto *asi = dyn_cast(&inst); if (!asi) continue; diff --git a/lib/SILOptimizer/UtilityPasses/OSSALifetimeAnalysis.cpp b/lib/SILOptimizer/UtilityPasses/OSSALifetimeAnalysis.cpp index c105685adee..48a695c4780 100644 --- a/lib/SILOptimizer/UtilityPasses/OSSALifetimeAnalysis.cpp +++ b/lib/SILOptimizer/UtilityPasses/OSSALifetimeAnalysis.cpp @@ -108,8 +108,8 @@ void OSSALifetimeAnalysis::run() { SmallVector traceValues; InstructionDeleter deleter; for (auto &block : function) { - for (SILInstruction *inst : deleter.updatingRange(&block)) { - if (auto *debugValue = dyn_cast(inst)) { + for (SILInstruction &inst : block.deletableInstructions()) { + if (auto *debugValue = dyn_cast(&inst)) { if (!debugValue->hasTrace()) continue; traceValues.push_back(debugValue->getOperand()); diff --git a/lib/SILOptimizer/UtilityPasses/ParseTestSpecification.cpp b/lib/SILOptimizer/UtilityPasses/ParseTestSpecification.cpp index 9c4bbfd209f..c7db16883a0 100644 --- a/lib/SILOptimizer/UtilityPasses/ParseTestSpecification.cpp +++ b/lib/SILOptimizer/UtilityPasses/ParseTestSpecification.cpp @@ -29,8 +29,8 @@ void findAndDeleteTraceValues(SILFunction *function, SmallVectorImpl &values) { InstructionDeleter deleter; for (auto &block : *function) { - for (SILInstruction *inst : deleter.updatingRange(&block)) { - if (auto *debugValue = dyn_cast(inst)) { + for (SILInstruction &inst : block.deletableInstructions()) { + if (auto *debugValue = dyn_cast(&inst)) { if (!debugValue->hasTrace()) continue; values.push_back(debugValue->getOperand()); @@ -591,8 +591,8 @@ void swift::test::getTestSpecifications( SmallVectorImpl &specifications) { InstructionDeleter deleter; for (auto &block : *function) { - for (SILInstruction *inst : deleter.updatingRange(&block)) { - if (auto *tsi = dyn_cast(inst)) { + for (SILInstruction &inst : block.deletableInstructions()) { + if (auto *tsi = dyn_cast(&inst)) { auto ref = tsi->getArgumentsSpecification(); auto *anchor = findAnchorInstructionAfter(tsi); specifications.push_back({std::string(ref.begin(), ref.end()), anchor}); From 9e0b86ddbdd60f0371ef69536f598165ff31d762 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 9 Dec 2022 14:01:17 +0100 Subject: [PATCH 6/8] Swift SIL: add `Builder.createUnreachable()` --- SwiftCompilerSources/Sources/SIL/Builder.swift | 8 ++++++++ include/swift/SIL/SILBridging.h | 1 + lib/SIL/Utils/SILBridging.cpp | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/SwiftCompilerSources/Sources/SIL/Builder.swift b/SwiftCompilerSources/Sources/SIL/Builder.swift index d2087e919f4..a56e112ef87 100644 --- a/SwiftCompilerSources/Sources/SIL/Builder.swift +++ b/SwiftCompilerSources/Sources/SIL/Builder.swift @@ -187,4 +187,12 @@ public struct Builder { return bi.getAs(BranchInst.self) } } + + @discardableResult + public func createUnreachable() -> UnreachableInst { + notifyInstructionsChanged() + notifyBranchesChanged() + let ui = SILBuilder_createUnreachable(bridged) + return ui.getAs(UnreachableInst.self) + } } diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index 3eb96d25c4b..325a216fe51 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -456,6 +456,7 @@ BridgedInstruction SILBuilder_createUncheckedEnumData(BridgedBuilder builder, BridgedInstruction SILBuilder_createBranch( BridgedBuilder builder, BridgedBasicBlock destBlock, BridgedValueArray arguments); +BridgedInstruction SILBuilder_createUnreachable(BridgedBuilder builder); SWIFT_END_NULLABILITY_ANNOTATIONS diff --git a/lib/SIL/Utils/SILBridging.cpp b/lib/SIL/Utils/SILBridging.cpp index 8ef9d269618..5bec1b4f31e 100644 --- a/lib/SIL/Utils/SILBridging.cpp +++ b/lib/SIL/Utils/SILBridging.cpp @@ -1184,3 +1184,9 @@ BridgedInstruction SILBuilder_createBranch( castToBasicBlock(destBlock), getSILValues(arguments, argValues))}; } + +BridgedInstruction SILBuilder_createUnreachable(BridgedBuilder b) { + SILBuilder builder(castToInst(b.insertBefore), castToBasicBlock(b.insertAtEnd), + b.loc.getScope()); + return {builder.createUnreachable(RegularLocation(b.loc.getLocation()))}; +} From a8ba48e71d6cb11936273653c92076cfcf00198c Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 9 Dec 2022 14:03:26 +0100 Subject: [PATCH 7/8] Swift SIL: add `Instruction.isDeleted` API --- SwiftCompilerSources/Sources/SIL/Instruction.swift | 4 ++++ include/swift/SIL/SILBridging.h | 1 + lib/SIL/Utils/SILBridging.cpp | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index 96ffdc61cc8..44e3f07e363 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -42,6 +42,10 @@ public class Instruction : ListNode, CustomStringConvertible, Hashable { return String(_cxxString: stdString) } + final public var isDeleted: Bool { + return SILInstruction_isDeleted(bridged) + } + final public var operands: OperandArray { return OperandArray(opArray: SILInstruction_getOperands(bridged)) } diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index 325a216fe51..994f940baa7 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -340,6 +340,7 @@ BridgedArgumentConvention SILArgument_getConvention(BridgedArgument argument); OptionalBridgedInstruction SILInstruction_next(BridgedInstruction inst); OptionalBridgedInstruction SILInstruction_previous(BridgedInstruction inst); BridgedBasicBlock SILInstruction_getParent(BridgedInstruction inst); +bool SILInstruction_isDeleted(BridgedInstruction inst); BridgedArrayRef SILInstruction_getOperands(BridgedInstruction inst); void SILInstruction_setOperand(BridgedInstruction inst, SwiftInt index, BridgedValue value); diff --git a/lib/SIL/Utils/SILBridging.cpp b/lib/SIL/Utils/SILBridging.cpp index 5bec1b4f31e..df22d5afe4a 100644 --- a/lib/SIL/Utils/SILBridging.cpp +++ b/lib/SIL/Utils/SILBridging.cpp @@ -743,6 +743,10 @@ BridgedBasicBlock SILInstruction_getParent(BridgedInstruction inst) { return {i->getParent()}; } +bool SILInstruction_isDeleted(BridgedInstruction inst) { + return castToInst(inst)->isDeleted(); +} + BridgedArrayRef SILInstruction_getOperands(BridgedInstruction inst) { auto operands = castToInst(inst)->getAllOperands(); return {(const unsigned char *)operands.data(), operands.size()}; From d4d1620f2866175e3a46fc872cac61f69fbff656 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 9 Dec 2022 14:08:12 +0100 Subject: [PATCH 8/8] Swift SIL: rework Instruction and BasicBlock lists to support deleting instructions during iteration Replace the generic `List` with the (non-generic) `InstructionList` and `BasicBlockList`. The `InstructionList` is now a bit different than the `BasicBlockList` because it supports that instructions are deleted while iterating over the list. Also add a test pass which tests instruction modification while iteration. --- .../DataStructures/InstructionRange.swift | 4 +- .../PassManager/PassRegistration.swift | 1 + .../Optimizer/TestPasses/CMakeLists.txt | 1 + .../TestPasses/TestInstructionIteration.swift | 77 ++++++ .../Sources/SIL/BasicBlock.swift | 71 +++++- .../Sources/SIL/Function.swift | 46 +++- .../Sources/SIL/Instruction.swift | 7 +- SwiftCompilerSources/Sources/SIL/Utils.swift | 65 ----- .../swift/SILOptimizer/PassManager/Passes.def | 2 + test/SIL/instruction_iteration.sil | 233 ++++++++++++++++++ 10 files changed, 423 insertions(+), 84 deletions(-) create mode 100644 SwiftCompilerSources/Sources/Optimizer/TestPasses/TestInstructionIteration.swift create mode 100644 test/SIL/instruction_iteration.sil diff --git a/SwiftCompilerSources/Sources/Optimizer/DataStructures/InstructionRange.swift b/SwiftCompilerSources/Sources/Optimizer/DataStructures/InstructionRange.swift index 614cfd4d368..daabdee86b7 100644 --- a/SwiftCompilerSources/Sources/Optimizer/DataStructures/InstructionRange.swift +++ b/SwiftCompilerSources/Sources/Optimizer/DataStructures/InstructionRange.swift @@ -94,7 +94,7 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren { var isValid: Bool { blockRange.isValid && // Check if there are any inserted instructions before the begin instruction in its block. - !ReverseList(first: begin).dropFirst().contains { insertedInsts.contains($0) } + !ReverseInstructionList(first: begin).dropFirst().contains { insertedInsts.contains($0) } } /// Returns the end instructions. @@ -115,7 +115,7 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren { /// Returns the interior instructions. var interiors: LazySequence, - LazyFilterSequence>>>> { + LazyFilterSequence>>> { blockRange.inserted.lazy.flatMap { var include = blockRange.contains($0) return $0.instructions.reversed().lazy.filter { diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift index 2956be7f4b5..7779fba34fa 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift @@ -75,6 +75,7 @@ private func registerSwiftPasses() { registerPass(deadEndBlockDumper, { deadEndBlockDumper.run($0) }) registerPass(rangeDumper, { rangeDumper.run($0) }) registerPass(runUnitTests, { runUnitTests.run($0) }) + registerPass(testInstructionIteration, { testInstructionIteration.run($0) }) } private func registerSwiftAnalyses() { diff --git a/SwiftCompilerSources/Sources/Optimizer/TestPasses/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/TestPasses/CMakeLists.txt index c503cb36ad6..3e92f820198 100644 --- a/SwiftCompilerSources/Sources/Optimizer/TestPasses/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/TestPasses/CMakeLists.txt @@ -14,4 +14,5 @@ swift_compiler_sources(Optimizer SILPrinter.swift RangeDumper.swift RunUnitTests.swift + TestInstructionIteration.swift ) diff --git a/SwiftCompilerSources/Sources/Optimizer/TestPasses/TestInstructionIteration.swift b/SwiftCompilerSources/Sources/Optimizer/TestPasses/TestInstructionIteration.swift new file mode 100644 index 00000000000..4f828a16315 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/TestPasses/TestInstructionIteration.swift @@ -0,0 +1,77 @@ +//===--- TestInstructionIteration.swift -----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +import SIL + +/// Tests instruction iteration while modifying the instruction list. +/// +/// This pass iterates over the instruction list of the function's block and performs +/// modifications of the instruction list - mostly deleting instructions. +/// Modifications are triggered by `string_literal` instructions with known "commands". +/// E.g. if a +/// ``` +/// %1 = string_literal utf8 "delete_strings" +/// ``` +/// is encountered during the iteration, it triggers the deletion of all `string_literal` +/// instructions of the basic block (including the current one). +let testInstructionIteration = FunctionPass(name: "test-instruction-iteration") { + (function: Function, context: PassContext) in + + print("Test instruction iteration in \(function.name):") + + let reverse = function.name.string.hasSuffix("backward") + + for block in function.blocks { + print("\(block.name):") + let termLoc = block.terminator.location + if reverse { + for inst in block.instructions.reversed() { + handle(instruction: inst, context) + } + } else { + for inst in block.instructions { + handle(instruction: inst, context) + } + } + if block.instructions.isEmpty || !(block.instructions.reversed().first is TermInst) { + let builder = Builder(atEndOf: block, location: termLoc, context) + builder.createUnreachable() + } + } + print("End function \(function.name):") +} + +private func handle(instruction: Instruction, _ context: PassContext) { + print(instruction) + if let sl = instruction as? StringLiteralInst { + switch sl.string { + case "delete_strings": + deleteAllInstructions(ofType: StringLiteralInst.self, in: instruction.block, context) + case "delete_ints": + deleteAllInstructions(ofType: IntegerLiteralInst.self, in: instruction.block, context) + case "delete_branches": + deleteAllInstructions(ofType: BranchInst.self, in: instruction.block, context) + case "split_block": + _ = context.splitBlock(at: instruction) + default: + break + } + } +} + +private func deleteAllInstructions(ofType: InstType.Type, in block: BasicBlock, _ context: PassContext) { + for inst in block.instructions { + if inst is InstType { + context.erase(instruction: inst) + } + } +} diff --git a/SwiftCompilerSources/Sources/SIL/BasicBlock.swift b/SwiftCompilerSources/Sources/SIL/BasicBlock.swift index 8ce02c92faf..e0661f0d9bb 100644 --- a/SwiftCompilerSources/Sources/SIL/BasicBlock.swift +++ b/SwiftCompilerSources/Sources/SIL/BasicBlock.swift @@ -13,15 +13,10 @@ import Basic import SILBridging -final public class BasicBlock : ListNode, CustomStringConvertible, HasShortDescription { +final public class BasicBlock : CustomStringConvertible, HasShortDescription { public var next: BasicBlock? { SILBasicBlock_next(bridged).block } public var previous: BasicBlock? { SILBasicBlock_previous(bridged).block } - // Needed for ReverseList.reversed(). Never use directly. - public var _firstInList: BasicBlock { SILFunction_firstBlock(function.bridged).block! } - // Needed for List.reversed(). Never use directly. - public var _lastInList: BasicBlock { SILFunction_lastBlock(function.bridged).block! } - public var function: Function { SILBasicBlock_getFunction(bridged).function } public var description: String { @@ -32,8 +27,8 @@ final public class BasicBlock : ListNode, CustomStringConvertible, HasShortDescr public var arguments: ArgumentArray { ArgumentArray(block: self) } - public var instructions: List { - List(first: SILBasicBlock_firstInst(bridged).instruction) + public var instructions: InstructionList { + InstructionList(first: SILBasicBlock_firstInst(bridged).instruction) } public var terminator: TermInst { @@ -75,6 +70,66 @@ final public class BasicBlock : ListNode, CustomStringConvertible, HasShortDescr public func == (lhs: BasicBlock, rhs: BasicBlock) -> Bool { lhs === rhs } public func != (lhs: BasicBlock, rhs: BasicBlock) -> Bool { lhs !== rhs } +/// The list of instructions in a BasicBlock. +/// +/// It's allowed to delete the current, next or any other instructions while +/// iterating over the instruction list. +public struct InstructionList : CollectionLikeSequence, IteratorProtocol { + private var currentInstruction: Instruction? + + public init(first: Instruction?) { currentInstruction = first } + + public mutating func next() -> Instruction? { + if var inst = currentInstruction { + while inst.isDeleted { + guard let nextInst = inst.next else { + return nil + } + inst = nextInst + } + currentInstruction = inst.next + return inst + } + return nil + } + + public var first: Instruction? { currentInstruction } + + public func reversed() -> ReverseInstructionList { + if let inst = currentInstruction { + let lastInst = SILBasicBlock_lastInst(inst.block.bridged).instruction + return ReverseInstructionList(first: lastInst) + } + return ReverseInstructionList(first: nil) + } +} + +/// The list of instructions in a BasicBlock in reverse order. +/// +/// It's allowed to delete the current, next or any other instructions while +/// iterating over the instruction list. +public struct ReverseInstructionList : CollectionLikeSequence, IteratorProtocol { + private var currentInstruction: Instruction? + + public init(first: Instruction?) { currentInstruction = first } + + public mutating func next() -> Instruction? { + if var inst = currentInstruction { + while inst.isDeleted { + guard let nextInst = inst.previous else { + return nil + } + inst = nextInst + } + currentInstruction = inst.previous + return inst + } + return nil + } + + public var first: Instruction? { currentInstruction } +} + public struct ArgumentArray : RandomAccessCollection { fileprivate let block: BasicBlock diff --git a/SwiftCompilerSources/Sources/SIL/Function.swift b/SwiftCompilerSources/Sources/SIL/Function.swift index 40048043586..c6125c96468 100644 --- a/SwiftCompilerSources/Sources/SIL/Function.swift +++ b/SwiftCompilerSources/Sources/SIL/Function.swift @@ -42,8 +42,8 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash SILFunction_firstBlock(bridged).block! } - public var blocks : List { - return List(first: SILFunction_firstBlock(bridged).block) + public var blocks : BasicBlockList { + BasicBlockList(first: SILFunction_firstBlock(bridged).block) } public var arguments: LazyMapSequence { @@ -51,7 +51,7 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash } /// All instructions of all blocks. - public var instructions: LazySequence, List>>> { + public var instructions: LazySequence>> { blocks.lazy.flatMap { $0.instructions } } @@ -358,3 +358,43 @@ public extension SideEffects.GlobalEffects { } } } + +public struct BasicBlockList : CollectionLikeSequence, IteratorProtocol { + private var currentBlock: BasicBlock? + + public init(first: BasicBlock?) { currentBlock = first } + + public mutating func next() -> BasicBlock? { + if let block = currentBlock { + currentBlock = block.next + return block + } + return nil + } + + public var first: BasicBlock? { currentBlock } + + public func reversed() -> ReverseBasicBlockList { + if let block = currentBlock { + let lastBlock = SILFunction_lastBlock(block.function.bridged).block + return ReverseBasicBlockList(first: lastBlock) + } + return ReverseBasicBlockList(first: nil) + } +} + +public struct ReverseBasicBlockList : CollectionLikeSequence, IteratorProtocol { + private var currentBlock: BasicBlock? + + public init(first: BasicBlock?) { currentBlock = first } + + public mutating func next() -> BasicBlock? { + if let block = currentBlock { + currentBlock = block.previous + return block + } + return nil + } + + public var first: BasicBlock? { currentBlock } +} diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index 44e3f07e363..16637f6b0ed 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -17,7 +17,7 @@ import SILBridging // Instruction base classes //===----------------------------------------------------------------------===// -public class Instruction : ListNode, CustomStringConvertible, Hashable { +public class Instruction : CustomStringConvertible, Hashable { final public var next: Instruction? { SILInstruction_next(bridged).instruction } @@ -26,11 +26,6 @@ public class Instruction : ListNode, CustomStringConvertible, Hashable { SILInstruction_previous(bridged).instruction } - // Needed for ReverseList.reversed(). Never use directly. - public var _firstInList: Instruction { SILBasicBlock_firstInst(block.bridged).instruction! } - // Needed for List.reversed(). Never use directly. - public var _lastInList: Instruction { SILBasicBlock_lastInst(block.bridged).instruction! } - final public var block: BasicBlock { SILInstruction_getParent(bridged).block } diff --git a/SwiftCompilerSources/Sources/SIL/Utils.swift b/SwiftCompilerSources/Sources/SIL/Utils.swift index 4070391905b..b0412631f48 100644 --- a/SwiftCompilerSources/Sources/SIL/Utils.swift +++ b/SwiftCompilerSources/Sources/SIL/Utils.swift @@ -17,71 +17,6 @@ import SILBridging // Otherwise The Optimizer would fall back to Swift's assert implementation. @_exported import Basic -//===----------------------------------------------------------------------===// -// Lists -//===----------------------------------------------------------------------===// - -public protocol ListNode : AnyObject { - associatedtype Element - var next: Element? { get } - var previous: Element? { get } - - /// The first node in the list. Used to implement `reversed()`. - var _firstInList: Element { get } - - /// The last node in the list. Used to implement `reversed()`. - var _lastInList: Element { get } -} - -public struct List : - CollectionLikeSequence, IteratorProtocol where NodeType.Element == NodeType { - private var currentNode: NodeType? - - public init(first: NodeType?) { currentNode = first } - - public mutating func next() -> NodeType? { - if let node = currentNode { - currentNode = node.next - return node - } - return nil - } - - public var first: NodeType? { currentNode } - - public func reversed() -> ReverseList { - if let node = first { - return ReverseList(first: node._lastInList) - } - return ReverseList(first: nil) - } -} - -public struct ReverseList : - CollectionLikeSequence, IteratorProtocol where NodeType.Element == NodeType { - private var currentNode: NodeType? - - public init(first: NodeType?) { currentNode = first } - - public mutating func next() -> NodeType? { - if let node = currentNode { - currentNode = node.previous - return node - } - return nil - } - - public var first: NodeType? { currentNode } - - public func reversed() -> ReverseList { - if let node = first { - return ReverseList(first: node._firstInList) - } - return ReverseList(first: nil) - } -} - - //===----------------------------------------------------------------------===// // Sequence Utilities //===----------------------------------------------------------------------===// diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 502dfb62b59..a6790e99a67 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -226,6 +226,8 @@ SWIFT_FUNCTION_PASS(ComputeEscapeEffects, "compute-escape-effects", "Computes function escape effects") SWIFT_FUNCTION_PASS(ComputeSideEffects, "compute-side-effects", "Computes function side effects") +SWIFT_FUNCTION_PASS(TestInstructionIteration, "test-instruction-iteration", + "Tests instruction iteration") PASS(FlowIsolation, "flow-isolation", "Enforces flow-sensitive actor isolation rules") PASS(FunctionOrderPrinter, "function-order-printer", diff --git a/test/SIL/instruction_iteration.sil b/test/SIL/instruction_iteration.sil new file mode 100644 index 00000000000..d3235cca356 --- /dev/null +++ b/test/SIL/instruction_iteration.sil @@ -0,0 +1,233 @@ +// RUN: %empty-directory(%t) +// RUN: %target-sil-opt %s -test-instruction-iteration -o %t/out.sil | %FileCheck --check-prefix=LOG %s +// RUN: %FileCheck %s < %t/out.sil + +// REQUIRES: swift_in_compiler + +sil_stage canonical + +import Builtin + +// LOG-LABEL: Test instruction iteration in delete_current_forward: +// LOG-NEXT: bb0: +// LOG-NEXT: %0 = tuple () +// LOG-NEXT: %1 = string_literal utf8 "delete_strings" +// LOG-NEXT: %1 = integer_literal $Builtin.Int64, 3 +// LOG-NEXT: return %0 : $() +// LOG-NEXT: End function delete_current_forward: + +// CHECK-LABEL: sil @delete_current_forward : $@convention(thin) () -> () { +// CHECK-NEXT: bb0: +// CHECK-NEXT: %0 = tuple () +// CHECK-NEXT: %1 = integer_literal $Builtin.Int64, 3 +// CHECK-NEXT: return %0 : $() +// CHECK-NEXT: } // end sil function 'delete_current_forward' +sil @delete_current_forward : $@convention(thin) () -> () { +bb0: + %0 = tuple () + %1 = string_literal utf8 "delete_strings" + %2 = integer_literal $Builtin.Int64, 3 + return %0 : $() +} + +// LOG-LABEL: Test instruction iteration in delete_next_forward: +// LOG-NEXT: bb0: +// LOG-NEXT: %0 = tuple () +// LOG-NEXT: %1 = string_literal utf8 "delete_ints" +// LOG-NEXT: return %0 : $() +// LOG-NEXT: End function delete_next_forward: + +// CHECK-LABEL: sil @delete_next_forward : $@convention(thin) () -> () { +// CHECK-NEXT: bb0: +// CHECK-NEXT: %0 = tuple () +// CHECK-NEXT: %1 = string_literal utf8 "delete_ints" +// CHECK-NEXT: return %0 : $() +// CHECK-NEXT: } // end sil function 'delete_next_forward' +sil @delete_next_forward : $@convention(thin) () -> () { +bb0: + %0 = tuple () + %1 = string_literal utf8 "delete_ints" + %2 = integer_literal $Builtin.Int64, 3 + return %0 : $() +} + +// LOG-LABEL: Test instruction iteration in delete_current_and_next_forward: +// LOG-NEXT: bb0: +// LOG-NEXT: %0 = tuple () +// LOG-NEXT: %1 = string_literal utf8 "delete_strings" +// LOG-NEXT: return %0 : $() +// LOG-NEXT: End function delete_current_and_next_forward: + +// CHECK-LABEL: sil @delete_current_and_next_forward : $@convention(thin) () -> () { +// CHECK-NEXT: bb0: +// CHECK-NEXT: %0 = tuple () +// CHECK-NEXT: return %0 : $() +// CHECK-NEXT: } // end sil function 'delete_current_and_next_forward' +sil @delete_current_and_next_forward : $@convention(thin) () -> () { +bb0: + %0 = tuple () + %1 = string_literal utf8 "delete_strings" + %2 = string_literal utf8 "" + return %0 : $() +} + +// LOG-LABEL: Test instruction iteration in delete_until_end_forward: +// LOG-NEXT: bb0: +// LOG-NEXT: %0 = tuple () +// LOG-NEXT: %1 = string_literal utf8 "delete_branches" +// LOG-NEXT: %2 = string_literal utf8 "delete_strings" +// LOG-NEXT: bb1: +// LOG-NEXT: return %0 : $() +// LOG-NEXT: End function delete_until_end_forward: + +// CHECK-LABEL: sil @delete_until_end_forward : $@convention(thin) () -> () { +// CHECK-NEXT: bb0: +// CHECK-NEXT: %0 = tuple () +// CHECK-NEXT: unreachable +// CHECK: } // end sil function 'delete_until_end_forward' +sil @delete_until_end_forward : $@convention(thin) () -> () { +bb0: + %0 = tuple () + %1 = string_literal utf8 "delete_branches" + %2 = string_literal utf8 "delete_strings" + %3 = string_literal utf8 "" + %4 = string_literal utf8 "" + br bb1 +bb1: + return %0 : $() +} + +// LOG-LABEL: Test instruction iteration in delete_current_backward: +// LOG-NEXT: bb0: +// LOG-NEXT: return %0 : $() +// LOG-NEXT: %2 = string_literal utf8 "delete_strings" +// LOG-NEXT: %1 = integer_literal $Builtin.Int64, 3 +// LOG-NEXT: %0 = tuple () +// LOG-NEXT: End function delete_current_backward: + +// CHECK-LABEL: sil @delete_current_backward : $@convention(thin) () -> () { +// CHECK-NEXT: bb0: +// CHECK-NEXT: %0 = tuple () +// CHECK-NEXT: %1 = integer_literal $Builtin.Int64, 3 +// CHECK-NEXT: return %0 : $() +// CHECK-NEXT: } // end sil function 'delete_current_backward' +sil @delete_current_backward : $@convention(thin) () -> () { +bb0: + %0 = tuple () + %1 = integer_literal $Builtin.Int64, 3 + %2 = string_literal utf8 "delete_strings" + return %0 : $() +} + +// LOG-LABEL: Test instruction iteration in delete_next_backward: +// LOG-NEXT: bb0: +// LOG-NEXT: return %0 : $() +// LOG-NEXT: %2 = string_literal utf8 "delete_ints" +// LOG-NEXT: %0 = tuple () +// LOG-NEXT: End function delete_next_backward: + +// CHECK-LABEL: sil @delete_next_backward : $@convention(thin) () -> () { +// CHECK-NEXT: bb0: +// CHECK-NEXT: %0 = tuple () +// CHECK-NEXT: %1 = string_literal utf8 "delete_ints" +// CHECK-NEXT: return %0 : $() +// CHECK-NEXT: } // end sil function 'delete_next_backward' +sil @delete_next_backward : $@convention(thin) () -> () { +bb0: + %0 = tuple () + %1 = integer_literal $Builtin.Int64, 3 + %2 = string_literal utf8 "delete_ints" + return %0 : $() +} + +// LOG-LABEL: Test instruction iteration in delete_current_and_next_backward: +// LOG-NEXT: bb0: +// LOG-NEXT: return %0 : $() +// LOG-NEXT: %2 = string_literal utf8 "delete_strings" +// LOG-NEXT: %0 = tuple () +// LOG-NEXT: End function delete_current_and_next_backward: + +// CHECK-LABEL: sil @delete_current_and_next_backward : $@convention(thin) () -> () { +// CHECK-NEXT: bb0: +// CHECK-NEXT: %0 = tuple () +// CHECK-NEXT: return %0 : $() +// CHECK-NEXT: } // end sil function 'delete_current_and_next_backward' +sil @delete_current_and_next_backward : $@convention(thin) () -> () { +bb0: + %0 = tuple () + %1 = string_literal utf8 "" + %2 = string_literal utf8 "delete_strings" + return %0 : $() +} + +// LOG-LABEL: Test instruction iteration in delete_until_end_backward: +// LOG-NEXT: bb0: +// LOG-NEXT: return %3 : $() +// LOG-NEXT: %3 = tuple () +// LOG-NEXT: %2 = string_literal utf8 "delete_strings" +// LOG-NEXT: End function delete_until_end_backward: + +// CHECK-LABEL: sil @delete_until_end_backward : $@convention(thin) () -> () { +// CHECK-NEXT: bb0: +// CHECK-NEXT: %0 = tuple () +// CHECK-NEXT: return %0 : $() +// CHECK-NEXT: } // end sil function 'delete_until_end_backward' +sil @delete_until_end_backward : $@convention(thin) () -> () { +bb0: + %0 = string_literal utf8 "" + %1 = string_literal utf8 "" + %2 = string_literal utf8 "delete_strings" + %3 = tuple () + return %3 : $() +} + +// LOG-LABEL: Test instruction iteration in split_block_forward: +// LOG-NEXT: bb0: +// LOG-NEXT: %0 = tuple () +// LOG-NEXT: %1 = string_literal utf8 "split_block" +// LOG-NEXT: %2 = integer_literal $Builtin.Int64, 3 +// LOG-NEXT: return %0 : $() +// LOG-NEXT: End function split_block_forward: + +// CHECK-LABEL: sil @split_block_forward : $@convention(thin) () -> () { +// CHECK-NEXT: bb0: +// CHECK-NEXT: %0 = tuple () +// CHECK-NEXT: unreachable +// CHECK: bb1: +// CHECK-NEXT: %2 = string_literal utf8 "split_block" +// CHECK-NEXT: %3 = integer_literal $Builtin.Int64, 3 +// CHECK-NEXT: return %0 : $() +// CHECK-NEXT: } // end sil function 'split_block_forward' +sil @split_block_forward : $@convention(thin) () -> () { +bb0: + %0 = tuple () + %1 = string_literal utf8 "split_block" + %2 = integer_literal $Builtin.Int64, 3 + return %0 : $() +} + +// LOG-LABEL: Test instruction iteration in split_block_backward: +// LOG-NEXT: bb0: +// LOG-NEXT: return %0 : $() +// LOG-NEXT: %2 = integer_literal $Builtin.Int64, 3 +// LOG-NEXT: %1 = string_literal utf8 "split_block" +// LOG-NEXT: %0 = tuple () +// LOG-NEXT: End function split_block_backward: + +// CHECK-LABEL: sil @split_block_backward : $@convention(thin) () -> () { +// CHECK-NEXT: bb0: +// CHECK-NEXT: %0 = tuple () +// CHECK-NEXT: unreachable +// CHECK: bb1: +// CHECK-NEXT: %2 = string_literal utf8 "split_block" +// CHECK-NEXT: %3 = integer_literal $Builtin.Int64, 3 +// CHECK-NEXT: return %0 : $() +// CHECK-NEXT: } // end sil function 'split_block_backward' +sil @split_block_backward : $@convention(thin) () -> () { +bb0: + %0 = tuple () + %1 = string_literal utf8 "split_block" + %2 = integer_literal $Builtin.Int64, 3 + return %0 : $() +} +