blob: 6df8e06cc0d5baf0c9a20440a1efd54bef806355 [file] [log] [blame]
//===--- SILInstructionWorklist.h -------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// When visiting the instructions in a function/basic block, one often modifies
/// the list of instructions to be visited. That fact introduces complexity in
/// the form of ensuring that no effort is wasted moving items down in the list
/// when an item is removed from it and also in the form of ensuring that no
/// instructions which have been modified/deleted are visited.
///
/// The SILInstructionWorklist manages that complexity. It is responsible for
/// ensuring that removing an instruction is not unnecessarily expensive and
/// that only valid instructions are removed from the list.
///
/// Additionally, SILInstructionWorklist provides conveniences for simple
/// instruction modifications and ensuring that the appropriate instructions
/// will be visited accordingly. For example, if provides a method for
/// replacing an operation which has already been removed with a new instruction
/// determined by a SILInstructionVisitor.
///
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SIL_SILINSTRUCTIONWORKLIST_H
#define SWIFT_SIL_SILINSTRUCTIONWORKLIST_H
#include "swift/Basic/BlotSetVector.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILValue.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
namespace swift {
class SILInstructionWorklistBase {
const char *loggingName;
protected:
SILInstructionWorklistBase(const char *loggingName)
: loggingName(loggingName){};
// Invokes the provided function with the debug stream and the previously
// specified logging name.
//
// Note: Because it contains LLVM_DEBUG, the definition is in the .cpp file.
// Consequently, we are relying on LTO to ensure that the calls are
// inlined and removed in non-assert builds.
void withDebugStream(
std::function<void(llvm::raw_ostream &stream, const char *loggingName)>
perform);
};
/// Manages a list of instructions awaiting visitation.
template <typename VectorT = std::vector<SILInstruction *>,
typename MapT = llvm::DenseMap<SILInstruction *, unsigned>>
class SILInstructionWorklist : SILInstructionWorklistBase {
BlotSetVector<SILInstruction *, VectorT, MapT> worklist;
void operator=(const SILInstructionWorklist &rhs) = delete;
SILInstructionWorklist(const SILInstructionWorklist &worklist) = delete;
public:
SILInstructionWorklist(const char *loggingName = "InstructionWorklist")
: SILInstructionWorklistBase(loggingName) {}
/// Returns true if the worklist is empty.
bool isEmpty() const { return worklist.empty(); }
/// Returns the number of elements in the worklist.
unsigned size() const { return worklist.size(); }
/// Add the specified instruction to the worklist if it isn't already in it.
void add(SILInstruction *instruction) {
if (worklist.insert(instruction).second) {
withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) {
stream << loggingName << ": ADD: " << *instruction << '\n';
});
}
}
/// If the given SILValue is a SILInstruction add it to the worklist.
void addValue(SILValue value) {
if (auto *instruction = value->getDefiningInstruction()) {
add(instruction);
}
}
/// Add the given list of instructions in reverse order to the worklist. This
/// routine assumes that the worklist is empty and the given list has no
/// duplicates.
void addInitialGroup(ArrayRef<SILInstruction *> list) {
assert(worklist.empty() && "worklist must be empty to add initial group");
worklist.reserve(list.size() + 16);
withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) {
stream << loggingName << ": ADDING: " << list.size()
<< " instrs to worklist\n";
});
while (!list.empty()) {
SILInstruction *instruction = list.back();
list = list.drop_back();
worklist.insert(instruction);
}
}
// If instruction is in the worklist, remove it.
void erase(SILInstruction *instruction) { worklist.erase(instruction); }
/// Remove the top element from the worklist.
SILInstruction *pop_back_val() {
return worklist.pop_back_val().getValueOr(nullptr);
}
/// When an instruction has been simplified, add all of its users to the
/// worklist, since additional simplifications of its users may have been
/// exposed.
void addUsersToWorklist(SILValue value) {
for (auto *use : value->getUses()) {
add(use->getUser());
}
}
/// When an instruction has been simplified, add all of its users to the
/// worklist, since additional simplifications of its users may have been
/// exposed.
void addUsersOfAllResultsToWorklist(SILInstruction *instruction) {
for (auto result : instruction->getResults()) {
addUsersToWorklist(&*result);
}
}
/// Check that the worklist is empty and nuke the backing store if it is
/// large.
void resetChecked() {
assert(worklist.empty() && "Vector empty, but the map is not?");
// Do an explicit clear, shrinking the storage if needed.
worklist.clear();
}
/// Find usages of \p instruction and replace them with usages of \p result.
///
/// Intended to be called during visitation after \p instruction has been
/// removed from the worklist.
///
/// \p instruction the instruction whose usages will be replaced
/// \p result the instruction whose usages will replace \p instruction
///
/// \return whether the instruction was deleted or modified.
bool replaceInstructionWithInstruction(SILInstruction *instruction,
SILInstruction *result
#ifndef NDEBUG
,
std::string instructionDescription
#endif
) {
if (result == instruction) {
#ifndef NDEBUG
withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) {
stream << loggingName << ": Mod = " << instructionDescription << '\n'
<< " "
<< " New = " << *instruction << '\n';
});
#endif
// If the instruction was modified, it's possible that it is now dead.
// if so, remove it.
if (isInstructionTriviallyDead(instruction)) {
eraseInstFromFunction(*instruction);
} else {
add(instruction);
addUsersOfAllResultsToWorklist(instruction);
}
return false;
}
assert(&*std::prev(instruction->getIterator()) == result &&
"Expected new instruction inserted before existing instruction!");
withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) {
stream << loggingName << ": Old = " << *instruction << '\n'
<< " "
<< " New = " << *result << '\n';
});
// Everything uses the new instruction now.
replaceInstUsesPairwiseWith(instruction, result);
// Push the new instruction and any users onto the worklist.
add(result);
addUsersOfAllResultsToWorklist(result);
eraseInstFromFunction(*instruction);
return true;
}
// Insert the instruction newInstruction before instruction old in old's
// parent block. Add newInstruction to the worklist.
SILInstruction *insertNewInstBefore(SILInstruction *newInstruction,
SILInstruction &old) {
assert(newInstruction && newInstruction->getParent() == nullptr &&
"newInstruction instruction already inserted into a basic block!");
SILBasicBlock *block = old.getParent();
block->insert(&old, newInstruction); // Insert inst
add(newInstruction);
return newInstruction;
}
// This method is to be used when an instruction is found to be dead,
// replaceable with another preexisting expression. Here we add all uses of
// instruction to the worklist, and replace all uses of instruction with the
// new value.
void replaceInstUsesWith(SingleValueInstruction &instruction,
ValueBase *value) {
addUsersToWorklist(&instruction); // Add all modified instrs to worklist.
withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) {
stream << loggingName << ": Replacing " << instruction << '\n'
<< " "
<< " with " << *value << '\n';
});
instruction.replaceAllUsesWith(value);
}
// This method is to be used when a value is found to be dead,
// replaceable with another preexisting expression. Here we add all
// uses of oldValue to the worklist, replace all uses of oldValue
// with newValue.
void replaceValueUsesWith(SILValue oldValue, SILValue newValue) {
addUsersToWorklist(oldValue); // Add all modified instrs to worklist.
withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) {
stream << loggingName << ": Replacing " << oldValue << '\n'
<< " "
<< " with " << newValue << '\n';
});
oldValue->replaceAllUsesWith(newValue);
}
void replaceInstUsesPairwiseWith(SILInstruction *oldI, SILInstruction *newI) {
withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) {
stream << loggingName << ": Replacing " << *oldI << '\n'
<< " "
<< " with " << *newI << '\n';
});
auto oldResults = oldI->getResults();
auto newResults = newI->getResults();
assert(oldResults.size() == newResults.size());
for (auto i : indices(oldResults)) {
// Add all modified instrs to worklist.
addUsersToWorklist(oldResults[i]);
oldResults[i]->replaceAllUsesWith(newResults[i]);
}
}
// Some instructions can never be "trivially dead" due to side effects or
// producing a void value. In those cases, visit methods should use this
// method to delete the given instruction.
void eraseInstFromFunction(SILInstruction &instruction,
SILBasicBlock::iterator &iterator,
bool addOperandsToWorklist = true) {
// Delete any debug users first.
for (auto result : instruction.getResults()) {
while (!result->use_empty()) {
auto *user = result->use_begin()->getUser();
assert(user->isDebugInstruction());
if (iterator == user->getIterator())
++iterator;
erase(user);
user->eraseFromParent();
}
}
if (iterator == instruction.getIterator())
++iterator;
eraseSingleInstFromFunction(instruction, addOperandsToWorklist);
}
void eraseInstFromFunction(SILInstruction &instruction,
bool addOperandsToWorklist = true) {
SILBasicBlock::iterator nullIter;
return eraseInstFromFunction(instruction, nullIter, addOperandsToWorklist);
}
void eraseSingleInstFromFunction(SILInstruction &instruction,
bool addOperandsToWorklist) {
withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) {
stream << loggingName << ": ERASE " << instruction << '\n';
});
assert(!instruction.hasUsesOfAnyResult() &&
"Cannot erase instruction that is used!");
// Make sure that we reprocess all operands now that we reduced their
// use counts.
if (instruction.getNumOperands() < 8 && addOperandsToWorklist) {
for (auto &operand : instruction.getAllOperands()) {
if (auto *operandInstruction =
operand.get()->getDefiningInstruction()) {
withDebugStream([&](llvm::raw_ostream &stream,
StringRef loggingName) {
stream << loggingName << ": add op " << *operandInstruction << '\n'
<< " from erased inst to worklist\n";
});
add(operandInstruction);
}
}
}
erase(&instruction);
instruction.eraseFromParent();
}
};
// TODO: This name is somewhat unpleasant. Once the type is templated over its
// storage and renamed BlottableWorklist, this type will be
// SmallBlottableWorklist which is more reasonable.
template <unsigned N>
class SmallSILInstructionWorklist final
: public SILInstructionWorklist<
llvm::SmallVector<Optional<SILInstruction *>, N>,
// TODO: A DenseMap rather than a SmallDenseMap is used here to avoid
// running into an upstream problem with the handling of grow()
// when it results in rehashing and tombstone removal:
//
// https://reviews.llvm.org/D56455
llvm::DenseMap<SILInstruction *, unsigned>> {
public:
using VectorT = llvm::SmallVector<Optional<SILInstruction *>, N>;
using MapT = llvm::DenseMap<SILInstruction *, unsigned>;
SmallSILInstructionWorklist(const char *loggingName = "InstructionWorklist")
: SILInstructionWorklist<VectorT, MapT>(loggingName) {}
};
} // end namespace swift
#endif // SWIFT_SIL_SILINSTRUCTIONWORKLIST_H