Files
swift-mirror/include/swift/SIL/BasicBlockUtils.h
Michael Gottesman 259d2bb182 [ownership] Commit a generic replaceAllUsesAndEraseFixingOwnership api and enable SimplifyInstruction on OSSA.
This is a generic API that when ownership is enabled allows one to replace all
uses of a value with a value with a differing ownership by transforming/lifetime
extending as appropriate.

This API supports all pairings of ownership /except/ replacing a value with
OwnershipKind::None with a value without OwnershipKind::None. This is a more
complex optimization that we do not support today. As a result, we include on
our state struct a helper routine that callers can use to know if the two values
that they want to process can be handled by the algorithm.

My moticiation is to use this to to update InstSimplify and SILCombiner in a
less bug prone way rather than just turn stuff off.

Noting that this transformation inserts ownership instructions, I have made sure
to test this API in two ways:

1. With Mandatory Combiner alone (to make sure it works period).

2. With Mandatory Combiner + Semantic ARC Opts to make sure that we can
   eliminate the extra ownership instructions it inserts.

As one can see from the tests, the optimizer today is able to handle all of
these transforms except one conditional case where I need to eliminate a dead
phi arg. I have a separate branch that hits that today but I have exposed unsafe
behavior in ClosureLifetimeFixup that I need to fix first before I can land
that. I don't want that to stop this PR since I think the current low level ARC
optimizer may be able to help me here since this is a simple transform it does
all of the time.
2020-12-09 11:53:56 -08:00

183 lines
7.0 KiB
C++

//===--- BasicBlockUtils.h - Utilities for SILBasicBlock -------*- 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SIL_BASICBLOCKUTILS_H
#define SWIFT_SIL_BASICBLOCKUTILS_H
#include "swift/SIL/SILValue.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
namespace swift {
class SILFunction;
class SILBasicBlock;
class TermInst;
class DominanceInfo;
class SILLoopInfo;
/// Replace a branch target.
///
/// \param T The terminating instruction to modify.
/// \param edgeIdx The successor edges index that will be replaced.
/// \param newDest The new target block.
/// \param preserveArgs If set, preserve arguments on the replaced edge.
void changeBranchTarget(TermInst *T, unsigned edgeIdx, SILBasicBlock *newDest,
bool preserveArgs);
/// Returns the arguments values on the specified CFG edge. If necessary, may
/// add create new SILPHIArguments, using `NewEdgeBB` as the placeholder.
void getEdgeArgs(TermInst *T, unsigned edgeIdx, SILBasicBlock *newEdgeBB,
llvm::SmallVectorImpl<SILValue> &args);
/// Splits the edge from terminator.
///
/// Also updates dominance and loop information if not null.
///
/// Returns the newly created basic block.
SILBasicBlock *splitEdge(TermInst *T, unsigned edgeIdx,
DominanceInfo *DT = nullptr,
SILLoopInfo *LI = nullptr);
/// Merge a basic block ending in a branch with its successor
/// if possible.
void mergeBasicBlockWithSingleSuccessor(SILBasicBlock *BB,
SILBasicBlock *succBB);
/// A utility for finding dead-end blocks.
///
/// Dead-end blocks are blocks from which there is no path to the function exit
/// (either return or throw). These are blocks which end with an unreachable
/// instruction and blocks from which all paths end in "unreachable" blocks.
/// This utility is needed to determine if the a value definition can have a
/// lack of users ignored along a specific path.
class DeadEndBlocks {
llvm::SetVector<const SILBasicBlock *> ReachableBlocks;
const SILFunction *F;
bool isComputed = false;
void compute();
public:
DeadEndBlocks(const SILFunction *F) : F(F) {}
/// Returns true if \p BB is a dead-end block.
bool isDeadEnd(const SILBasicBlock *block) {
if (!isComputed) {
// Lazily compute the dataflow.
compute();
isComputed = true;
}
return ReachableBlocks.count(block) == 0;
}
bool empty() {
if (!isComputed) {
// Lazily compute the dataflow.
compute();
isComputed = true;
}
return ReachableBlocks.empty();
}
const SILFunction *getFunction() const { return F; }
};
/// A struct that contains the intermediate state used in computing
/// joint-dominance sets. Enables a pass to easily reuse the same small data
/// structures with clearing (noting that clearing our internal state does not
/// cause us to shrink meaning that once we malloc, we keep the malloced
/// memory).
struct JointPostDominanceSetComputer {
/// The worklist that drives the algorithm.
SmallVector<SILBasicBlock *, 32> worklist;
/// A set that guards our worklist. Any block before it is added to worklist
/// should be checked against visitedBlocks.
SmallPtrSet<SILBasicBlock *, 32> visitedBlocks;
/// The set of blocks where we begin our walk.
SmallPtrSet<SILBasicBlock *, 8> initialBlocks;
/// A subset of our initial blocks that we found as a predecessor of another
/// block along our walk.
SmallVector<SILBasicBlock *, 8> reachableInputBlocks;
/// As we process the worklist, any successors that we see that have not been
/// visited yet are placed in here. At the end of our worklist, any blocks
/// that remain here are "leaking blocks" that together with our initial set
/// would provide a jointly-postdominating set of our dominating value.
SmallSetVector<SILBasicBlock *, 8> blocksThatLeakIfNeverVisited;
DeadEndBlocks &deadEndBlocks;
JointPostDominanceSetComputer(DeadEndBlocks &deadEndBlocks)
: deadEndBlocks(deadEndBlocks) {}
void clear() {
worklist.clear();
visitedBlocks.clear();
initialBlocks.clear();
reachableInputBlocks.clear();
blocksThatLeakIfNeverVisited.clear();
}
/// Compute joint-postdominating set for \p dominatingBlock and \p
/// dominatedBlockSet found by walking up the CFG from the latter to the
/// former.
///
/// We pass back the following information via callbacks so our callers can
/// use whatever container they need to:
///
/// * inputBlocksFoundDuringWalk: Any blocks from the "dominated
/// block set" that was found as a predecessor block during our traversal is
/// passed to this callback. These can occur for two reasons:
///
/// 1. We actually had a block in \p dominatedBlockSet that was reachable
/// from another block in said set. This is a valid usage of the API
/// since it could be that the user does not care about such uses and
/// leave this callback empty.
///
/// 2. We had a block in \p dominatedBlockSet that is in a sub-loop in the
/// loop-nest relative to \p dominatingBlock causing us to go around a
/// backedge and hit the block during our traversal. In this case, we
/// have already during the traversal passed the exiting blocks of the
/// sub-loop as joint postdominace completion set blocks. This is useful
/// if one is using this API for lifetime extension purposes of lifetime
/// ending uses and one needs to insert compensating copy_value at these
/// locations due to the lack of strong control-equivalence in between
/// the block and \p dominatingBlock.
///
///
/// * foundJointPostDomSetCompletionBlocks: The set of blocks not in \p
/// dominatedBlockSet that together with \p dominatedBlockSet
/// jointly-postdominate \p dominatedBlock. This is "completing" the joint
/// post-dominance set.
///
/// * inputBlocksInJointPostDomSet: Any of our input blocks that were never
/// found as a predecessor is passed to this callback. This block is in the
/// final minimal joint-postdominance set and is passed to this
/// callback. This is optional and we will avoid doing work if it is not
/// set.
void findJointPostDominatingSet(
SILBasicBlock *dominatingBlock,
ArrayRef<SILBasicBlock *> dominatedBlockSet,
function_ref<void(SILBasicBlock *)> inputBlocksFoundDuringWalk,
function_ref<void(SILBasicBlock *)> foundJointPostDomSetCompletionBlocks,
function_ref<void(SILBasicBlock *)> inputBlocksInJointPostDomSet = {});
};
} // namespace swift
#endif