Files
swift-mirror/include/swift/SILOptimizer/Utils/UpdatingInstructionIterator.h
2021-11-18 11:38:08 -08:00

283 lines
9.9 KiB
C++

//===--- 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 <typename IteratorBase, bool IsReverse, typename Registry>
class UpdatingListIterator {
using Self = UpdatingListIterator<IteratorBase, IsReverse, Registry>;
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 &registry, IteratorBase base)
: registry(&registry), 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->regsitry = 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 <typename OtherIteratorBase>
explicit UpdatingListIterator(
const UpdatingListIterator<OtherIteratorBase, !IsReverse, Registry> &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<SILBasicBlock::iterator, false,
UpdatingInstructionIteratorRegistry>;
using UpdatingReverseInstructionIterator =
UpdatingListIterator<SILBasicBlock::reverse_iterator, true,
UpdatingInstructionIteratorRegistry>;
/// Track instruction iterators that need updating when intructions 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 modificaiton 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<UpdatingInstructionIterator *, 4> forwardIterators;
SmallVector<UpdatingReverseInstructionIterator *, 4> reverseIterators;
/// Callbacks used when adding/deleting instructions.
InstModCallbacks callbacks;
public:
UpdatingInstructionIteratorRegistry(
InstModCallbacks chainedCallbacks = InstModCallbacks()) {
rechainCallbacks(chainedCallbacks);
}
// The callbacks capture 'this'. So copying is invalid.
UpdatingInstructionIteratorRegistry(
const UpdatingInstructionIteratorRegistry &) = delete;
UpdatingInstructionIteratorRegistry &
operator=(const UpdatingInstructionIteratorRegistry &) = delete;
InstModCallbacks &getCallbacks() { return callbacks; }
void rechainCallbacks(InstModCallbacks chainedCallbacks) {
// Copy the two std::functions that we need. The rest of the callbacks are
// copied implicitly by assignment.
auto chainedDelete = chainedCallbacks.deleteInstFunc;
auto chainedNew = chainedCallbacks.createdNewInstFunc;
callbacks = chainedCallbacks
.onDelete([this, chainedDelete](SILInstruction *toDelete) {
notifyDelete(toDelete);
if (chainedDelete) {
chainedDelete(toDelete);
return;
}
toDelete->eraseFromParent();
})
.onCreateNewInst(
[this, chainedNew](SILInstruction *newlyCreatedInst) {
notifyNew(newlyCreatedInst);
if (chainedNew) {
chainedNew(newlyCreatedInst);
}
});
}
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<UpdatingInstructionIterator>
makeIteratorRange(SILBasicBlock *bb) {
return {makeIterator(bb->begin()), makeIterator(bb->end())};
}
llvm::iterator_range<UpdatingReverseInstructionIterator>
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