mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
* access * accessed * accesses * accessor * acquiring * across * activated * additive * address * addresses' * aggregated * analysis * and * appropriately * archetype * argument * associated * availability * barriers * because * been * beginning * belongs * beneficial * blocks * borrow * builtin * cannot * canonical * canonicalize * clazz * cleanup * coalesceable * coalesced * comparisons * completely * component * computed * concrete * conjunction * conservatively * constituent * construct * consuming * containing * covered * creates * critical * dataflow * declaration * defined * defining * definition * deinitialization * deliberately * dependencies * dependent * deserialized * destroy * deterministic * deterministically * devirtualizes * diagnostic * diagnostics * differentiation * disable * discipline * dominate * dominates * don't * element * eliminate * eliminating * elimination * embedded * encounter * epilogue * epsilon * escape * escaping * essential * evaluating * evaluation * evaluator * executing * existential * existentials * explicit * expression * extended * extension * extract * for * from * function * generic * guarantee * guaranteed * happened * heuristic * however * identifiable * immediately * implementation * improper * include * infinite * initialize * initialized * initializer * inside * instruction * interference * interferes * interleaved * internal * intersection * intractable * intrinsic * invalidates * irreducible * irrelevant * language * lifetime * literal * looks * materialize * meaning * mergeable * might * mimics * modification * modifies * multiple * mutating * necessarily * necessary * needsmultiplecopies * nonetheless * nothing * occurred * occurs * optimization * optimizing * original * outside * overflow * overlapping * overridden * owned * ownership * parallel * parameter * paths * patterns * pipeline * plottable * possible * potentially * practically * preamble * precede * preceding * predecessor * preferable * preparation * probably * projection * properties * property * protocol * reabstraction * reachable * recognized * recursive * recursively * redundant * reentrancy * referenced * registry * reinitialization * reload * represent * requires * response * responsible * retrieving * returned * returning * returns * rewriting * rewritten * sample * scenarios * scope * should * sideeffects * similar * simplify * simplifycfg * somewhat * spaghetti * specialization * specializations * specialized * specially * statistically * substitute * substitution * succeeds * successful * successfully * successor * superfluous * surprisingly * suspension * swift * targeted * that * that our * the * therefore * this * those * threshold * through * transform * transformation * truncated * ultimate * unchecked * uninitialized * unlikely * unmanaged * unoptimized key * updataflow * usefulness * utilities * villain * whenever * writes Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
279 lines
11 KiB
C++
279 lines
11 KiB
C++
//===--- InstModCallbacks.h - instruction modification callbacks -*- 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// InstModCallbacks: callbacks for instruction modification.
|
|
///
|
|
/// Callbacks are generally problematic because a pass cannot anticipate the
|
|
/// state that SIL will be in when lower-level utilities invoke the
|
|
/// callback. This creates implicit coupling across the layers of SIL utilities.
|
|
///
|
|
/// Alternatives:
|
|
///
|
|
/// For an Analyses that caches SILInstruction pointers, check
|
|
/// SILInstruction::isDeleted() upon retrieval, and use the PassManager's
|
|
/// analysis invalidation mechanism to clear the pointers at the end of each
|
|
/// pass. The pointers remain valid in the "deleted" state until the end the
|
|
/// pass.
|
|
///
|
|
/// For iterating over instructions during instruction creation and deletion,
|
|
/// use an UpdatingInstructionIterator provided by the InstructionDeleter
|
|
/// object:
|
|
///
|
|
/// for (SILInstruction *inst : deleter.updatingRange(bb)) ...
|
|
///
|
|
/// Make sure the pass uses the same deleter object for all deletion within the
|
|
/// iterator scope.
|
|
///
|
|
/// To defer instruction deletion so that deletions happen in bulk at a
|
|
/// convenient point in the pass, use InstructionDeleter::trackIfDead() and
|
|
/// cleanupDeadInstructions().
|
|
///
|
|
/// To determine whether multiple layers of utilities made any change to the
|
|
/// SIL, have each utility report whether it made a change.
|
|
///
|
|
/// For uses that don't fall into the categories above, restructure the pass so
|
|
/// that low-level operations on individual instructions don't require
|
|
/// callbacks. The SILCombine worklist is currently the main client of
|
|
/// callbacks. It's possible to work around this by running more complex
|
|
/// utilities as a separate SILCombine subpass in between draining the worklist
|
|
/// so those utilities do not require callbacks.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include <functional>
|
|
|
|
#ifndef SWIFT_SILOPTIMIZER_UTILS_INSTMODCALLBACKS_H
|
|
#define SWIFT_SILOPTIMIZER_UTILS_INSTMODCALLBACKS_H
|
|
|
|
namespace swift {
|
|
|
|
/// A structure containing callbacks that are called when an instruction is
|
|
/// removed or added.
|
|
///
|
|
/// PERFORMANCE NOTES: This code can be used in loops, so we want to make sure
|
|
/// to not have overhead when the user does not specify a callback. To do that
|
|
/// instead of defining a "default" std::function, we represent the "default"
|
|
/// functions as nullptr. Then, in the helper function trampoline that actually
|
|
/// gets called, we check if we have a nullptr and if we do, we perform the
|
|
/// default operation inline. What is nice about this from a perf perspective is
|
|
/// that in a loop this property should predict well since you have a single
|
|
/// branch that is going to go the same way everytime.
|
|
struct InstModCallbacks {
|
|
/// A function that is called to notify that a new function was created.
|
|
///
|
|
/// Default implementation is a no-op, but we still mark madeChange.
|
|
std::function<void(SILInstruction *newlyCreatedInst)> createdNewInstFunc;
|
|
|
|
/// A function sets the value in \p use to be \p newValue.
|
|
///
|
|
/// Default implementation just calls use->set(newValue).
|
|
///
|
|
/// NOTE: It is assumed that this operation will never invalidate instruction
|
|
/// iterators.
|
|
///
|
|
/// This can have compile-time implications and should be avoided
|
|
/// whenever possible in favor of more structured optimization passes.
|
|
std::function<void(Operand *use, SILValue newValue)> setUseValueFunc;
|
|
|
|
/// A function that takes in an instruction and deletes the inst.
|
|
///
|
|
/// This is used to invalidate dangling instruction pointers. The SIL will be
|
|
/// invalid when it is invoked. The callback is only allowed to inspect the
|
|
/// inline fields of \p instToDelete and iterate over the results. It is not
|
|
/// allowed to dereference operands or iterate uses.
|
|
///
|
|
/// See comments for notifyWillBeDeletedFunc.
|
|
///
|
|
/// The default implementation is:
|
|
///
|
|
/// instToDelete->eraseFromParent();
|
|
///
|
|
/// The reason this callback is responsible for deleting the instruction is to
|
|
/// interoperate more easily with
|
|
/// CanonicalizeInstruction::killInstruction(). This allows updates to choose
|
|
/// whether to happen before or after deleting the instruction and possibly
|
|
/// keep it around as a zombie object. All implementations must at least
|
|
/// immediately remove all references to the instruction, including the parent
|
|
/// block list.
|
|
///
|
|
/// TODO: Now that instructions deletion can be delayed via
|
|
/// SILModule::scheduleForDeletion(); there's no longer a good use case for
|
|
/// calling eraseFromParent() within this callback. Rewrite all clients
|
|
/// without doing the instruction deletion within the callback.
|
|
std::function<void(SILInstruction *instToDelete)> deleteInstFunc;
|
|
|
|
/// If non-null, called before a salient instruction is deleted or has its
|
|
/// references dropped. If null, no-op.
|
|
///
|
|
/// This can be used to respond to dead instructions that will be deleted in
|
|
/// the future. Unlike deleteInstFunc, the SIL will be in a valid
|
|
/// state. However, arbitrary SIL transformations may happen between this
|
|
/// invocation and actual instruction deletion.
|
|
///
|
|
/// This callback is not guaranteed to be called for every deleted
|
|
/// instruction. It cannot be used to invalidate dangling pointers. It is only
|
|
/// called for "salient" instructions that likely create additional
|
|
/// optimization opportunities when deleted. If a dead def-use chain is
|
|
/// deleted, notification only occurs for the initial def.
|
|
///
|
|
/// This is used in rare circumstances to update an optimization worklist. It
|
|
/// should be avoided whenever possible in favor of more structured
|
|
/// optimization passes.
|
|
std::function<void(SILInstruction *instThatWillBeDeleted)>
|
|
notifyWillBeDeletedFunc;
|
|
|
|
/// A boolean that tracks if any of our callbacks were ever called.
|
|
bool wereAnyCallbacksInvoked = false;
|
|
|
|
InstModCallbacks() = default;
|
|
~InstModCallbacks() = default;
|
|
InstModCallbacks(const InstModCallbacks &) = default;
|
|
|
|
/// Return a copy of self with deleteInstFunc set to \p newDeleteInstFunc.
|
|
LLVM_ATTRIBUTE_UNUSED InstModCallbacks
|
|
onDelete(decltype(deleteInstFunc) newDeleteInstFunc) const {
|
|
InstModCallbacks result = *this;
|
|
result.deleteInstFunc = newDeleteInstFunc;
|
|
return result;
|
|
}
|
|
|
|
/// Return a copy of self with createdNewInstFunc set to \p
|
|
/// newCreatedNewInstFunc.
|
|
LLVM_ATTRIBUTE_UNUSED InstModCallbacks
|
|
onCreateNewInst(decltype(createdNewInstFunc) newCreatedNewInstFunc) const {
|
|
InstModCallbacks result = *this;
|
|
result.createdNewInstFunc = newCreatedNewInstFunc;
|
|
return result;
|
|
}
|
|
|
|
/// Return a copy of self with setUseValueFunc set to \p newSetUseValueFunc.
|
|
LLVM_ATTRIBUTE_UNUSED InstModCallbacks
|
|
onSetUseValue(decltype(setUseValueFunc) newSetUseValueFunc) const {
|
|
InstModCallbacks result = *this;
|
|
result.setUseValueFunc = newSetUseValueFunc;
|
|
return result;
|
|
}
|
|
|
|
/// Return a copy of self with notifyWillBeDeletedFunc set to \p
|
|
/// newNotifyWillBeDeletedFunc.
|
|
LLVM_ATTRIBUTE_UNUSED
|
|
InstModCallbacks onNotifyWillBeDeleted(
|
|
decltype(notifyWillBeDeletedFunc) newNotifyWillBeDeletedFunc) const {
|
|
InstModCallbacks result = *this;
|
|
result.notifyWillBeDeletedFunc = newNotifyWillBeDeletedFunc;
|
|
return result;
|
|
}
|
|
|
|
void deleteInst(SILInstruction *instToDelete,
|
|
bool notifyWhenDeleting = true) {
|
|
wereAnyCallbacksInvoked = true;
|
|
if (notifyWhenDeleting && notifyWillBeDeletedFunc)
|
|
notifyWillBeDeletedFunc(instToDelete);
|
|
if (deleteInstFunc)
|
|
return deleteInstFunc(instToDelete);
|
|
instToDelete->eraseFromParent();
|
|
}
|
|
|
|
void createdNewInst(SILInstruction *newlyCreatedInst) {
|
|
wereAnyCallbacksInvoked = true;
|
|
if (createdNewInstFunc)
|
|
createdNewInstFunc(newlyCreatedInst);
|
|
}
|
|
|
|
void setUseValue(Operand *use, SILValue newValue) {
|
|
wereAnyCallbacksInvoked = true;
|
|
if (setUseValueFunc)
|
|
return setUseValueFunc(use, newValue);
|
|
use->set(newValue);
|
|
}
|
|
|
|
/// Notify via our callbacks that an instruction will be deleted/have its
|
|
/// operands dropped.
|
|
///
|
|
/// DISCUSSION: Since we do not delete instructions in any specific order, we
|
|
/// drop all references of the instructions before we call deleteInst. Thus
|
|
/// one can not in deleteInst look at operands. Certain parts of the optimizer
|
|
/// rely on this ability, so we preserve it.
|
|
void notifyWillBeDeleted(SILInstruction *instThatWillBeDeleted) {
|
|
wereAnyCallbacksInvoked = true;
|
|
if (notifyWillBeDeletedFunc)
|
|
return notifyWillBeDeletedFunc(instThatWillBeDeleted);
|
|
}
|
|
|
|
void replaceValueUsesWith(SILValue oldValue, SILValue newValue) {
|
|
wereAnyCallbacksInvoked = true;
|
|
|
|
// If setUseValueFunc is not set, just call RAUW directly. RAUW in this case
|
|
// is equivalent to what we do below. We just enable better
|
|
// performance. This ensures that the default InstModCallback is really
|
|
// fast.
|
|
if (!setUseValueFunc)
|
|
return oldValue->replaceAllUsesWith(newValue);
|
|
|
|
while (!oldValue->use_empty()) {
|
|
auto *use = *oldValue->use_begin();
|
|
setUseValue(use, newValue);
|
|
}
|
|
}
|
|
|
|
/// Replace all uses of the results of \p oldInst pairwise with new uses of
|
|
/// the results of \p newInst.
|
|
///
|
|
/// If \p setUseValueFunc is not set to a value, we just call inline
|
|
/// SILInstruction::replaceAllUsesPairwiseWith(...) to ensure we only pay a
|
|
/// cost if we actually set setUseValueFunc.
|
|
void replaceAllInstUsesPairwiseWith(SILInstruction *oldInst, SILInstruction *newInst) {
|
|
wereAnyCallbacksInvoked = true;
|
|
|
|
// If setUseValueFunc is not set, just call RAUW directly. RAUW in this case
|
|
// is equivalent to what we do below. We just enable better
|
|
// performance. This ensures that the default InstModCallback is really
|
|
// fast.
|
|
if (!setUseValueFunc)
|
|
return oldInst->replaceAllUsesPairwiseWith(newInst);
|
|
|
|
auto results = oldInst->getResults();
|
|
|
|
// If we don't have any results, fast-path out without asking the other
|
|
// instruction for its results.
|
|
if (results.empty()) {
|
|
assert(newInst->getResults().empty());
|
|
return;
|
|
}
|
|
|
|
// Replace values with the corresponding values of the other instruction.
|
|
auto otherResults = newInst->getResults();
|
|
assert(results.size() == otherResults.size());
|
|
for (auto i : indices(results)) {
|
|
replaceValueUsesWith(results[i], otherResults[i]);
|
|
}
|
|
}
|
|
|
|
void eraseAndRAUWSingleValueInst(SingleValueInstruction *oldInst,
|
|
SILValue newValue) {
|
|
wereAnyCallbacksInvoked = true;
|
|
replaceValueUsesWith(oldInst, newValue);
|
|
deleteInst(oldInst);
|
|
}
|
|
|
|
bool hadCallbackInvocation() const { return wereAnyCallbacksInvoked; }
|
|
|
|
/// Set \p wereAnyCallbacksInvoked to false. Useful if one wants to reuse an
|
|
/// InstModCallback in between iterations.
|
|
void resetHadCallbackInvocation() { wereAnyCallbacksInvoked = false; }
|
|
};
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|