mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Specifically before this PR, if a caller did not customize a specific callback of InstModCallbacks, we would store a static default std::function into InstModCallbacks. This means that we always would have an indirect jump. That is unfortunate since this code is often called in loops. In this PR, I eliminate this problem by: 1. I made all of the actual callback std::function in InstModCallback private and gave them a "Func" postfix (e.x.: deleteInst -> deleteInstFunc). 2. I created public methods with the old callback names to actually call the callbacks. This ensured that as long as we are not escaping callbacks from InstModCallback, this PR would not result in the need for any source changes since we are changing a call of a std::function field to a call to a method. 3. I changed all of the places that were escaping inst mod's callbacks to take an InstModCallback. We shouldn't be doing that anyway. 4. I changed the default value of each callback in InstModCallbacks to be a nullptr and changed the public helper methods to check if a callback is null. If the callback is not null, it is called, otherwise the getter falls back to an inline default implementation of the operation. All together this means that the cost of a plain InstModCallback is reduced and one pays an indirect function cost price as one customizes it further which is better scalability. P.S. as a little extra thing, I added a madeChange field onto the InstModCallback. Now that we have the helpers calling the callbacks, I can easily insert instrumentation like this, allowing for users to pass in InstModCallback and see if anything was RAUWed without needing to specify a callback.
221 lines
9.5 KiB
C++
221 lines
9.5 KiB
C++
//===--- CFGOptUtils.h - SIL CFG edge utilities -----------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2019 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// APIs used by the SILOptimizer for low-level branch and CFG edge analysis
|
|
/// and operations. These may merge blocks, split blocks, or create empty
|
|
/// blocks, but don't duplicate whole blocks.
|
|
///
|
|
/// Essential CFG utilities are in SIL/CFG.h.
|
|
///
|
|
/// Whole block-level transformations are in BasicBlockOptUtils.h.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SILOPTIMIZER_UTILS_CFGOPTUTILS_H
|
|
#define SWIFT_SILOPTIMIZER_UTILS_CFGOPTUTILS_H
|
|
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
|
|
|
|
namespace llvm {
|
|
template <typename T> class TinyPtrVector;
|
|
}
|
|
|
|
namespace swift {
|
|
|
|
class DominanceInfo;
|
|
class SILLoop;
|
|
class SILLoopInfo;
|
|
class InstModCallbacks;
|
|
|
|
/// Adds a new argument to an edge between a branch and a destination
|
|
/// block. Allows for user injected callbacks via \p callbacks.
|
|
///
|
|
/// \param branch The terminator to add the argument to.
|
|
/// \param dest The destination block of the edge.
|
|
/// \param val The value to the arguments of the branch.
|
|
/// \return The created branch. The old branch is deleted.
|
|
/// The argument is appended at the end of the argument tuple.
|
|
TermInst *addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest,
|
|
SILValue val, InstModCallbacks &callbacks);
|
|
|
|
/// Adds a new argument to an edge between a branch and a destination
|
|
/// block.
|
|
///
|
|
/// \param branch The terminator to add the argument to.
|
|
/// \param dest The destination block of the edge.
|
|
/// \param val The value to the arguments of the branch.
|
|
/// \return The created branch. The old branch is deleted.
|
|
/// The argument is appended at the end of the argument tuple.
|
|
inline TermInst *addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest,
|
|
SILValue val) {
|
|
InstModCallbacks callbacks;
|
|
return addNewEdgeValueToBranch(branch, dest, val, callbacks);
|
|
}
|
|
|
|
/// Changes the edge value between a branch and destination basic block
|
|
/// at the specified index. Changes all edges from \p Branch to \p Dest to carry
|
|
/// the value.
|
|
///
|
|
/// \param branch The branch to modify.
|
|
/// \param dest The destination of the edge.
|
|
/// \param idx The index of the argument to modify.
|
|
/// \param val The new value to use.
|
|
/// \return The new branch. Deletes the old one.
|
|
TermInst *changeEdgeValue(TermInst *branch, SILBasicBlock *dest, size_t idx,
|
|
SILValue val);
|
|
|
|
/// Deletes the edge value between a branch and a destination basic block at the
|
|
/// specified index. Asserts internally that the argument along the edge does
|
|
/// not have uses.
|
|
TermInst *deleteEdgeValue(TermInst *branch, SILBasicBlock *destBlock,
|
|
size_t argIndex);
|
|
|
|
/// Erase the \p argIndex phi argument from \p block. Asserts that the argument
|
|
/// is a /real/ phi argument. Removes all incoming values for the argument from
|
|
/// predecessor terminators. Asserts internally that it only ever is given
|
|
/// "true" phi argument.
|
|
void erasePhiArgument(SILBasicBlock *block, unsigned argIndex);
|
|
|
|
/// Replace a branch target.
|
|
///
|
|
/// \param t The terminating instruction to modify.
|
|
/// \param oldDest The successor block that will be replaced.
|
|
/// \param newDest The new target block.
|
|
/// \param preserveArgs If set, preserve arguments on the replaced edge.
|
|
void replaceBranchTarget(TermInst *t, SILBasicBlock *oldDest,
|
|
SILBasicBlock *newDest, bool preserveArgs);
|
|
|
|
/// Check if the edge from the terminator is critical.
|
|
bool isCriticalEdge(TermInst *t, unsigned edgeIdx);
|
|
|
|
inline bool isNonCriticalEdge(SILBasicBlock *predBB, SILBasicBlock *succBB) {
|
|
return predBB->getSingleSuccessorBlock() == succBB
|
|
|| succBB->getSinglePredecessorBlock() == predBB;
|
|
}
|
|
|
|
/// Splits the edge from terminator if it is critical.
|
|
///
|
|
/// Updates dominance information and loop information if not null.
|
|
/// Returns the newly created basic block on success or nullptr otherwise (if
|
|
/// the edge was not critical).
|
|
SILBasicBlock *splitCriticalEdge(TermInst *, unsigned edgeIdx,
|
|
DominanceInfo *domInfo = nullptr,
|
|
SILLoopInfo *loopInfo = nullptr);
|
|
|
|
/// Splits the critical edges between from and to. This code assumes there is
|
|
/// exactly one edge between the two basic blocks. It will return the wrong
|
|
/// result if there are multiple edges and will assert if there are no edges in
|
|
/// between the two blocks.
|
|
///
|
|
/// Updates dominance information and loop information if not null.
|
|
SILBasicBlock *splitIfCriticalEdge(SILBasicBlock *from, SILBasicBlock *to,
|
|
DominanceInfo *domInfo = nullptr,
|
|
SILLoopInfo *loopInfo = nullptr);
|
|
|
|
/// Splits all critical edges originating from `fromBB`.
|
|
bool splitCriticalEdgesFrom(SILBasicBlock *fromBB,
|
|
DominanceInfo *domInfo = nullptr,
|
|
SILLoopInfo *loopInfo = nullptr);
|
|
|
|
/// Splits all critical edges that have `toBB` as a destination.
|
|
bool splitCriticalEdgesTo(SILBasicBlock *toBB, DominanceInfo *domInfo = nullptr,
|
|
SILLoopInfo *loopInfo = nullptr);
|
|
|
|
/// Splits the edges between two basic blocks.
|
|
///
|
|
/// Updates dominance information and loop information if not null.
|
|
void splitEdgesFromTo(SILBasicBlock *from, SILBasicBlock *to,
|
|
DominanceInfo *domInfo = nullptr,
|
|
SILLoopInfo *loopInfo = nullptr);
|
|
|
|
/// Create a basic block to serve as the target of a conditional branch, or
|
|
/// other terminator with multiple successors. This avoids introducing critical
|
|
/// edges when inserting conditional branches.
|
|
///
|
|
/// This is a lightweight helper that assumes no block arguments and does not
|
|
/// update dominators or loops. For more general functionality, just create the
|
|
/// conditional branch then call splitCriticalEdge to fix it up.
|
|
SILBasicBlock *createSplitBranchTarget(SILBasicBlock *targetBlock,
|
|
SILBuilder &builder,
|
|
SILLocation loc);
|
|
|
|
/// Splits the basic block before the instruction with an unconditional branch
|
|
/// and updates the dominator tree and loop info. Returns the new, branched to
|
|
/// block that contains the end of \p SplitBeforeInst's block.
|
|
SILBasicBlock *splitBasicBlockAndBranch(SILBuilder &builder,
|
|
SILInstruction *splitBeforeInst,
|
|
DominanceInfo *domInfo,
|
|
SILLoopInfo *loopInfo);
|
|
|
|
/// Return true if the function has a critical edge, false otherwise.
|
|
bool hasCriticalEdges(SILFunction &f, bool onlyNonCondBr);
|
|
|
|
/// Split all critical edges in the given function, updating the
|
|
/// dominator tree and loop information if they are provided.
|
|
///
|
|
/// FIXME: This should never be called! Fix passes that create critical edges.
|
|
bool splitAllCriticalEdges(SILFunction &F, DominanceInfo *domInfo,
|
|
SILLoopInfo *loopInfo);
|
|
|
|
/// Split all cond_br critical edges with non-trivial arguments in the
|
|
/// function updating the dominator tree and loop information (if they are not
|
|
/// set to null).
|
|
///
|
|
/// A current invariant of Ownership SIL is that cond_br can only have critical
|
|
/// edges with non-trivial arguments. This simplifies computation.
|
|
bool splitAllCondBrCriticalEdgesWithNonTrivialArgs(SILFunction &fn,
|
|
DominanceInfo *domInfo,
|
|
SILLoopInfo *loopInfo);
|
|
|
|
/// Merge a basic block ending in a branch with its successor
|
|
/// if possible. If dominance information or loop info is non null update it.
|
|
/// Return true if block was merged.
|
|
bool mergeBasicBlockWithSuccessor(SILBasicBlock *bb, DominanceInfo *domInfo,
|
|
SILLoopInfo *loopInfo);
|
|
|
|
/// Merge basic blocks in the given function by eliminating all unconditional
|
|
/// branches to single-predecessor branch targets.
|
|
///
|
|
/// During optimization, SimplifyCFG also handles this, but this is a basic
|
|
/// canonicalization after any pass that splits blocks, such as inlining. This
|
|
/// is not done on-the-fly after splitting blocks because merging is linear in
|
|
/// the number of instructions, so interleaved merging and splitting is
|
|
/// quadratic.
|
|
bool mergeBasicBlocks(SILFunction *f);
|
|
|
|
/// Return true if we conservatively find all bb's that are non-failure exit
|
|
/// basic blocks and place them in \p bbs. If we find something we don't
|
|
/// understand, bail.
|
|
///
|
|
/// A non-failure exit BB is defined as a BB that:
|
|
///
|
|
/// 1. Has a return terminator.
|
|
/// 2. unreachable + noreturn terminator sequence.
|
|
/// 3. has a throw terminator.
|
|
///
|
|
/// If we just have an unreachable without a noreturn call before it, we must
|
|
/// have a failure BB.
|
|
///
|
|
/// We use a TinyPtrVector since in most cases this will only return one
|
|
/// SILBasicBlock since non-failure noreturn functions should not occur often
|
|
/// implying in most cases this will be one element.
|
|
///
|
|
/// TODO:
|
|
bool findAllNonFailureExitBBs(SILFunction *f,
|
|
llvm::TinyPtrVector<SILBasicBlock *> &bbs);
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|