Skip to content

[SIL] Extracted instruction methods from SILCombiner. #27042

New issue

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

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

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions include/swift/SIL/SILInstructionWorklist.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,18 @@
/// 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.
///
//===----------------------------------------------------------------------===//

#include "swift/Basic/BlotSetVector.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILValue.h"
#include "swift/SILOptimizer/Utils/Local.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"

Expand Down Expand Up @@ -126,6 +133,181 @@ class SILInstructionWorklist : SILInstructionWorklistBase {
// 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 NDNEBUG
,
std::string instructionDescription
#endif
) {
if (result != instruction) {
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;
} else {
withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) {
stream << loggingName << ": Mod = " << instructionDescription << '\n'
<< " "
<< " New = " << *instruction << '\n';
});

// 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;
}
}

// 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
Expand Down
143 changes: 8 additions & 135 deletions lib/SILOptimizer/SILCombiner/SILCombine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,29 +101,6 @@ void SILCombiner::addReachableCodeToWorklist(SILBasicBlock *BB) {
addInitialGroup(InstrsForSILCombineWorklist);
}

static void eraseSingleInstFromFunction(
SILInstruction &I,
SmallSILInstructionWorklist<256> &Worklist,
bool AddOperandsToWorklist) {
LLVM_DEBUG(llvm::dbgs() << "SC: ERASE " << I << '\n');

assert(!I.hasUsesOfAnyResult() && "Cannot erase instruction that is used!");

// Make sure that we reprocess all operands now that we reduced their
// use counts.
if (I.getNumOperands() < 8 && AddOperandsToWorklist) {
for (auto &OpI : I.getAllOperands()) {
if (auto *Op = OpI.get()->getDefiningInstruction()) {
LLVM_DEBUG(llvm::dbgs() << "SC: add op " << *Op
<< " from erased inst to worklist\n");
Worklist.add(Op);
}
}
}
Worklist.erase(&I);
I.eraseFromParent();
}

//===----------------------------------------------------------------------===//
// Implementation
//===----------------------------------------------------------------------===//
Expand All @@ -147,8 +124,8 @@ class SILCombineCanonicalize final : CanonicalizeInstruction {
// Just delete the given 'inst' and record its operands. The callback isn't
// allowed to mutate any other instructions.
void killInstruction(SILInstruction *inst) override {
eraseSingleInstFromFunction(*inst, Worklist,
/*AddOperandsToWorklist*/ true);
Worklist.eraseSingleInstFromFunction(*inst,
/*AddOperandsToWorklist*/ true);
changed = true;
}

Expand Down Expand Up @@ -216,34 +193,12 @@ bool SILCombiner::doOneIteration(SILFunction &F, unsigned Iteration) {
if (SILInstruction *Result = visit(I)) {
++NumCombined;
// Should we replace the old instruction with a new one?
if (Result != I) {
assert(&*std::prev(SILBasicBlock::iterator(I)) == Result &&
"Expected new instruction inserted before existing instruction!");

LLVM_DEBUG(llvm::dbgs() << "SC: Old = " << *I << '\n'
<< " New = " << *Result << '\n');

// Everything uses the new instruction now.
replaceInstUsesPairwiseWith(I, Result);

// Push the new instruction and any users onto the worklist.
Worklist.add(Result);
Worklist.addUsersOfAllResultsToWorklist(Result);

eraseInstFromFunction(*I);
} else {
LLVM_DEBUG(llvm::dbgs() << "SC: Mod = " << OrigI << '\n'
<< " New = " << *I << '\n');

// If the instruction was modified, it's possible that it is now dead.
// if so, remove it.
if (isInstructionTriviallyDead(I)) {
eraseInstFromFunction(*I);
} else {
Worklist.add(I);
Worklist.addUsersOfAllResultsToWorklist(I);
}
}
Worklist.replaceInstructionWithInstruction(I, Result
#ifndef NDEBUG
,
OrigI
#endif
);
MadeChange = true;
}

Expand Down Expand Up @@ -278,88 +233,6 @@ bool SILCombiner::runOnFunction(SILFunction &F) {
return Changed;
}

// Insert the instruction New before instruction Old in Old's parent BB. Add
// New to the worklist.
SILInstruction *SILCombiner::insertNewInstBefore(SILInstruction *New,
SILInstruction &Old) {
assert(New && New->getParent() == nullptr &&
"New instruction already inserted into a basic block!");
SILBasicBlock *BB = Old.getParent();
BB->insert(&Old, New); // Insert inst
Worklist.add(New);
return New;
}

// 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 I
// to the worklist, replace all uses of I with the new value, then return I,
// so that the combiner will know that I was modified.
void SILCombiner::replaceInstUsesWith(SingleValueInstruction &I, ValueBase *V) {
Worklist.addUsersToWorklist(&I); // Add all modified instrs to worklist.

LLVM_DEBUG(llvm::dbgs() << "SC: Replacing " << I << "\n"
<< " with " << *V << '\n');

I.replaceAllUsesWith(V);
}

void SILCombiner::replaceValueUsesWith(SILValue oldValue, SILValue newValue) {
Worklist.addUsersToWorklist(oldValue); // Add all modified instrs to worklist.

LLVM_DEBUG(llvm::dbgs() << "SC: Replacing " << oldValue << "\n"
<< " with " << newValue << '\n');

oldValue->replaceAllUsesWith(newValue);
}

/// Replace all of the results of the old instruction with the
/// corresponding results of the new instruction.
void SILCombiner::replaceInstUsesPairwiseWith(SILInstruction *oldI,
SILInstruction *newI) {
LLVM_DEBUG(llvm::dbgs() << "SC: 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.
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, since we cannot rely on
// SILCombines trivially dead instruction DCE in order to delete the
// instruction, visit methods should use this method to delete the given
// instruction and upon completion of their peephole return the value returned
// by this method.
SILInstruction *
SILCombiner::eraseInstFromFunction(SILInstruction &I,
SILBasicBlock::iterator &InstIter,
bool AddOperandsToWorklist) {
// Delete any debug users first.
for (auto result : I.getResults()) {
while (!result->use_empty()) {
auto *user = result->use_begin()->getUser();
assert(user->isDebugInstruction());
if (InstIter == user->getIterator())
++InstIter;
Worklist.erase(user);
user->eraseFromParent();
}
}
if (InstIter == I.getIterator())
++InstIter;

eraseSingleInstFromFunction(I, Worklist, AddOperandsToWorklist);
MadeChange = true;
// Dummy return, so the caller doesn't need to explicitly return nullptr.
return nullptr;
}

//===----------------------------------------------------------------------===//
// Entry Points
//===----------------------------------------------------------------------===//
Expand Down
Loading