Skip to content

[CanonicalizeOSSALifetime] Run on lexical lifetimes. #63606

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
98 changes: 74 additions & 24 deletions include/swift/SILOptimizer/Analysis/Reachability.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ class IterativeBackwardReachability final {
BasicBlockSet unknownEndBlocks;

public:
/// The blocks found between the gens and the defBlock into which
/// The blocks found between the gens and the initialBlocks into which
/// reachability may extend.
BasicBlockSetVector discoveredBlocks;
/// The sublist of gens which are killed within the blocks where they occur.
Expand Down Expand Up @@ -296,15 +296,32 @@ class IterativeBackwardReachability final {
};

/// Construct a dataflow for the specified function to run from the gens
/// provided by \p effects to the specified def block. So that the result
/// structure can be owned by the caller, it is taken by reference here.
/// provided by \p effects to the specified \p initialBlocks. So that the
/// result structure can be owned by the caller, it is taken by reference
/// here.
///
/// If a nullptr defBlock is specified, the dataflow may run up to the begin
/// of the function.
IterativeBackwardReachability(SILFunction *function, SILBasicBlock *defBlock,
/// If \p initialBlocks is empty, the dataflow may run up to the begin of the
/// function.
IterativeBackwardReachability(SILFunction *function,
ArrayRef<SILBasicBlock *> initialBlocks,
Effects &effects, Result &result)
: function(function), defBlock(defBlock), effects(effects),
result(result), dataflowWorklist(function) {}
: function(function), initialBlocks(function), effects(effects),
result(result), dataflowWorklist(function) {
for (auto *block : initialBlocks) {
this->initialBlocks.insert(block);
}
}

/// Convenience constructor to pass a single initial block.
static IterativeBackwardReachability
untilInitialBlock(SILFunction *function, SILBasicBlock *initialBlock,
Effects &effects, Result &result) {
using InitialBlocks = ArrayRef<SILBasicBlock *>;
InitialBlocks initialBlocks =
initialBlock ? InitialBlocks(initialBlock) : InitialBlocks();
return IterativeBackwardReachability(function, initialBlocks, effects,
result);
}

/// Step 1: Prepare to run the global dataflow: discover and summarize the
/// blocks in the relevant region.
Expand Down Expand Up @@ -358,9 +375,8 @@ class IterativeBackwardReachability final {

/// The function in which the dataflow will run.
SILFunction *function;
/// The block containing the def for the value--the dataflow will not
/// propagate beyond this block.
SILBasicBlock *defBlock;
/// The blocks beyond which the dataflow will not propagate.
BasicBlockSet initialBlocks;

/// Input to the dataflow.
Effects &effects;
Expand All @@ -375,9 +391,9 @@ class IterativeBackwardReachability final {
/// Current activity of the dataflow.
Stage stage = Stage::Unstarted;

/// Whether the def effectively occurs within the specified block.
bool isEffectiveDefBlock(SILBasicBlock *block) {
return defBlock ? block == defBlock : block == &*function->begin();
/// Whether dataflow continues beyond this block.
bool stopAtBlock(SILBasicBlock *block) {
return initialBlocks.contains(block) || &*function->begin() == block;
}

/// Form the meet of the end state of the provided predecessor with the begin
Expand Down Expand Up @@ -424,8 +440,8 @@ class IterativeBackwardReachability final {
/// effect of each block for use by the dataflow.
///
/// Starting from the gens, find all blocks which might be reached up to and
/// including the defBlock. Summarize the effects of these blocks along the
/// way.
/// including the initialBlocks. Summarize the effects of these blocks along
/// the way.
template <typename Effects>
void IterativeBackwardReachability<Effects>::initialize() {
assert(stage == Stage::Unstarted);
Expand Down Expand Up @@ -454,9 +470,9 @@ void IterativeBackwardReachability<Effects>::initialize() {
// adjacent successors.
continue;
}
if (isEffectiveDefBlock(block)) {
// If this block is the effective def block, dataflow mustn't propagate
// a reachable state through this block to its predecessors.
if (stopAtBlock(block)) {
// If dataflow is to stop at this block, it mustn't propagate a reachable
// state through this block to its predecessors.
continue;
}
for (auto *predecessor : block->getPredecessorBlocks())
Expand Down Expand Up @@ -631,9 +647,9 @@ void IterativeBackwardReachability<Effects>::solve() {
template <typename Effects>
void IterativeBackwardReachability<Effects>::propagateIntoPredecessors(
SILBasicBlock *successor) {
// State isn't tracked above the def block. Don't propagate state changes
// into its predecessors.
if (isEffectiveDefBlock(successor))
// State isn't tracked above the blocks dataflow stops at. Don't propagate
// state changes into its predecessors.
if (stopAtBlock(successor))
return;
assert(result.getBeginStateForBlock(successor) == State::Unreachable() &&
"propagating unreachability into predecessors of block whose begin is "
Expand Down Expand Up @@ -761,7 +777,6 @@ bool IterativeBackwardReachability<Effects>::findBarrier(SILInstruction *from,
if (!effect)
continue;
if (effect == Effect::Gen()) {
assert(false && "found gen (before kill) in reachable block");
continue;
}
// effect == Effect::Kill
Expand All @@ -777,7 +792,7 @@ bool IterativeBackwardReachability<Effects>::findBarrier(SILInstruction *from,
}
}
assert(result.getEffectForBlock(block) != Effect::Kill());
if (isEffectiveDefBlock(block)) {
if (stopAtBlock(block)) {
visitor.visitBarrierBlock(block);
return true;
}
Expand Down Expand Up @@ -888,6 +903,41 @@ void IterativeBackwardReachability<Effects>::Result::setEffectForBlock(
}
}

//===----------------------------------------------------------------------===//
// MARK: findBarriersBackward
//===----------------------------------------------------------------------===//

using llvm::ArrayRef;
using llvm::function_ref;

struct ReachableBarriers final {
/// Instructions which are barriers.
llvm::SmallVector<SILInstruction *, 4> instructions;

/// Blocks one of whose phis is a barrier.
llvm::SmallVector<SILBasicBlock *, 4> phis;

/// Boundary edges; edges such that
/// (1) the target block is reachable-at-begin
/// (2) at least one adjacent edge's target is not reachable-at-begin.
llvm::SmallVector<SILBasicBlock *, 4> edges;

ReachableBarriers() {}
ReachableBarriers(ReachableBarriers const &) = delete;
ReachableBarriers &operator=(ReachableBarriers const &) = delete;
};

/// Walk backwards from the specified \p roots through at the earliest \p
/// initialBlocks to populate \p barriers by querying \p isBarrier along the
/// way.
///
/// If \p initialBlocks is empty, dataflow continues to the begin of the
/// function.
void findBarriersBackward(ArrayRef<SILInstruction *> roots,
ArrayRef<SILBasicBlock *> initialBlocks,
SILFunction &function, ReachableBarriers &barriers,
function_ref<bool(SILInstruction *)> isBarrier);

} // end namespace swift

#endif
13 changes: 11 additions & 2 deletions include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@

namespace swift {

class BasicCalleeAnalysis;

extern llvm::Statistic NumCopiesAndMovesEliminated;
extern llvm::Statistic NumCopiesGenerated;

Expand Down Expand Up @@ -225,6 +227,8 @@ class CanonicalizeOSSALifetime final {

DominanceInfo *domTree = nullptr;

BasicCalleeAnalysis *calleeAnalysis;

InstructionDeleter &deleter;

/// The SILValue to canonicalize.
Expand Down Expand Up @@ -296,10 +300,12 @@ class CanonicalizeOSSALifetime final {
CanonicalizeOSSALifetime(bool pruneDebugMode, bool maximizeLifetime,
SILFunction *function,
NonLocalAccessBlockAnalysis *accessBlockAnalysis,
DominanceInfo *domTree, InstructionDeleter &deleter)
DominanceInfo *domTree,
BasicCalleeAnalysis *calleeAnalysis,
InstructionDeleter &deleter)
: pruneDebugMode(pruneDebugMode), maximizeLifetime(maximizeLifetime),
accessBlockAnalysis(accessBlockAnalysis), domTree(domTree),
deleter(deleter) {}
calleeAnalysis(calleeAnalysis), deleter(deleter) {}

SILValue getCurrentDef() const { return currentDef; }

Expand Down Expand Up @@ -405,6 +411,9 @@ class CanonicalizeOSSALifetime final {
void findExtendedBoundary(PrunedLivenessBoundary const &originalBoundary,
PrunedLivenessBoundary &boundary);

void findDestroysOutsideBoundary(SmallVectorImpl<SILInstruction *> &destroys);
void extendLivenessToDeinitBarriers();

void extendUnconsumedLiveness(PrunedLivenessBoundary const &boundary);

void insertDestroysOnBoundary(PrunedLivenessBoundary const &boundary);
Expand Down
1 change: 1 addition & 0 deletions lib/SILOptimizer/Analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ target_sources(swiftSILOptimizer PRIVATE
NonLocalAccessBlockAnalysis.cpp
PassManagerVerifierAnalysis.cpp
ProtocolConformanceAnalysis.cpp
Reachability.cpp
RCIdentityAnalysis.cpp
SimplifyInstruction.cpp
TypeExpansionAnalysis.cpp
Expand Down
109 changes: 109 additions & 0 deletions lib/SILOptimizer/Analysis/Reachability.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//=----------- Reachability.cpp - Walking from roots to barriers. -----------=//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 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
//
//===----------------------------------------------------------------------===//

#include "swift/SILOptimizer/Analysis/Reachability.h"

using namespace swift;

/// Walks backwards from the specified roots to find barrier instructions, phis,
/// and blocks via the isBarrier predicate.
///
/// Implements IterativeBackwardReachability::Effects
/// Implements IterativeBackwardReachability::findBarriers::Visitor
class FindBarriersBackwardDataflow final {
using Reachability =
IterativeBackwardReachability<FindBarriersBackwardDataflow>;
using Effect = Reachability::Effect;
ArrayRef<SILInstruction *> const roots;
function_ref<bool(SILInstruction *)> isBarrier;
ReachableBarriers &barriers;
Reachability::Result result;
Reachability reachability;

public:
FindBarriersBackwardDataflow(SILFunction &function,
ArrayRef<SILInstruction *> roots,
ArrayRef<SILBasicBlock *> stopBlocks,
ReachableBarriers &barriers,
function_ref<bool(SILInstruction *)> isBarrier)
: roots(roots), isBarrier(isBarrier), barriers(barriers),
result(&function), reachability(&function, stopBlocks, *this, result) {}
FindBarriersBackwardDataflow(FindBarriersBackwardDataflow const &) = delete;
FindBarriersBackwardDataflow &
operator=(FindBarriersBackwardDataflow const &) = delete;

void run();

private:
friend Reachability;

/// IterativeBackwardReachability::Effects

auto gens() { return roots; }

Effect effectForInstruction(SILInstruction *);
Effect effectForPhi(SILBasicBlock *);

auto localGens() { return result.localGens; }

bool isLocalGen(SILInstruction *instruction) {
return result.localGens.contains(instruction);
}

/// IterativeBackwardReachability::findBarriers::Visitor

void visitBarrierInstruction(SILInstruction *instruction) {
barriers.instructions.push_back(instruction);
}

void visitBarrierPhi(SILBasicBlock *block) { barriers.phis.push_back(block); }

void visitBarrierBlock(SILBasicBlock *block) {
barriers.edges.push_back(block);
}
};

FindBarriersBackwardDataflow::Effect
FindBarriersBackwardDataflow::effectForInstruction(
SILInstruction *instruction) {
if (llvm::is_contained(roots, instruction))
return Effect::Gen();
auto barrier = isBarrier(instruction);
return barrier ? Effect::Kill() : Effect::NoEffect();
}

FindBarriersBackwardDataflow::Effect
FindBarriersBackwardDataflow::effectForPhi(SILBasicBlock *block) {
assert(llvm::all_of(block->getArguments(),
[&](auto argument) { return PhiValue(argument); }));

bool barrier =
llvm::any_of(block->getPredecessorBlocks(), [&](auto *predecessor) {
return isBarrier(predecessor->getTerminator());
});
return barrier ? Effect::Kill() : Effect::NoEffect();
}

void FindBarriersBackwardDataflow::run() {
reachability.initialize();
reachability.solve();
reachability.findBarriers(*this);
}

void swift::findBarriersBackward(
ArrayRef<SILInstruction *> roots, ArrayRef<SILBasicBlock *> initialBlocks,
SILFunction &function, ReachableBarriers &barriers,
function_ref<bool(SILInstruction *)> isBarrier) {
FindBarriersBackwardDataflow flow(function, roots, initialBlocks, barriers,
isBarrier);
flow.run();
}
3 changes: 2 additions & 1 deletion lib/SILOptimizer/Mandatory/MoveOnlyObjectCheckerUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ struct OSSACanonicalizer {
InstructionDeleter &deleter)
: canonicalizer(false /*pruneDebugMode*/,
!fn->shouldOptimize() /*maximizeLifetime*/, fn,
nullptr /*accessBlockAnalysis*/, domTree, deleter) {}
nullptr /*accessBlockAnalysis*/, domTree,
nullptr /*calleeAnalysis*/, deleter) {}

void clear() {
consumingUsesNeedingCopy.clear();
Expand Down
3 changes: 2 additions & 1 deletion lib/SILOptimizer/SILCombiner/SILCombine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ SILCombiner::SILCombiner(SILFunctionTransform *trans,
bool removeCondFails, bool enableCopyPropagation) :
parentTransform(trans),
AA(trans->getPassManager()->getAnalysis<AliasAnalysis>(trans->getFunction())),
CA(trans->getPassManager()->getAnalysis<BasicCalleeAnalysis>()),
DA(trans->getPassManager()->getAnalysis<DominanceAnalysis>()),
PCA(trans->getPassManager()->getAnalysis<ProtocolConformanceAnalysis>()),
CHA(trans->getPassManager()->getAnalysis<ClassHierarchyAnalysis>()),
Expand Down Expand Up @@ -352,7 +353,7 @@ void SILCombiner::canonicalizeOSSALifetimes(SILInstruction *currentInst) {
CanonicalizeOSSALifetime canonicalizer(
false /*prune debug*/,
!parentTransform->getFunction()->shouldOptimize() /*maximize lifetime*/,
parentTransform->getFunction(), NLABA, domTree, deleter);
parentTransform->getFunction(), NLABA, domTree, CA, deleter);
CanonicalizeBorrowScope borrowCanonicalizer(parentTransform->getFunction(),
deleter);

Expand Down
7 changes: 5 additions & 2 deletions lib/SILOptimizer/SILCombiner/SILCombiner.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,22 @@

#include "swift/Basic/Defer.h"
#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILInstructionWorklist.h"
#include "swift/SIL/SILValue.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h"
#include "swift/SILOptimizer/Analysis/ProtocolConformanceAnalysis.h"
#include "swift/SILOptimizer/OptimizerBridging.h"
#include "swift/SILOptimizer/PassManager/PassManager.h"
#include "swift/SILOptimizer/Utils/CastOptimizer.h"
#include "swift/SILOptimizer/Utils/Existential.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
#include "swift/SILOptimizer/PassManager/PassManager.h"

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
Expand All @@ -55,6 +56,8 @@ class SILCombiner :

AliasAnalysis *AA;

BasicCalleeAnalysis *CA;

DominanceAnalysis *DA;

/// Determine the set of types a protocol conforms to in whole-module
Expand Down
Loading