Skip to content

[ownership] Add a DeadEndBlocksAnalysis that vends/saves DeadEndBlocks in between passes. #35477

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
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
21 changes: 13 additions & 8 deletions include/swift/SIL/BasicBlockUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,26 +62,31 @@ void mergeBasicBlockWithSingleSuccessor(SILBasicBlock *BB,
/// 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;
llvm::SetVector<const SILBasicBlock *> reachableBlocks;
const SILFunction *f;
bool didComputeValue = false;

void compute();

public:
DeadEndBlocks(const SILFunction *F) : F(F) {}
DeadEndBlocks(const SILFunction *f) : f(f) {}

/// Returns true if \p BB is a dead-end block.
bool isDeadEnd(const SILBasicBlock *block) {
if (!isComputed) {
if (!didComputeValue) {
// Lazily compute the dataflow.
compute();
isComputed = true;
didComputeValue = true;
}
return ReachableBlocks.count(block) == 0;
return reachableBlocks.count(block) == 0;
}

const SILFunction *getFunction() const { return F; }
/// Return true if this dead end blocks has computed its internal cache yet.
///
/// Used to determine if we need to verify a DeadEndBlocks.
bool isComputed() const { return didComputeValue; }

const SILFunction *getFunction() const { return f; }
};

/// A struct that contains the intermediate state used in computing
Expand Down
1 change: 1 addition & 0 deletions include/swift/SILOptimizer/Analysis/Analysis.def
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@ ANALYSIS(RCIdentity)
ANALYSIS(SideEffect)
ANALYSIS(TypeExpansion)
ANALYSIS(PassManagerVerifier)
ANALYSIS(DeadEndBlocks)

#undef ANALYSIS
47 changes: 47 additions & 0 deletions include/swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//===--- DeadEndBlocksAnalysis.h ------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_SILOPTIMIZER_DEADENDBLOCKSANALYSIS_H
#define SWIFT_SILOPTIMIZER_DEADENDBLOCKSANALYSIS_H

#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SILOptimizer/Analysis/Analysis.h"

namespace swift {

class DeadEndBlocksAnalysis final : public FunctionAnalysisBase<DeadEndBlocks> {
public:
DeadEndBlocksAnalysis()
: FunctionAnalysisBase<DeadEndBlocks>(SILAnalysisKind::DeadEndBlocks) {}

DeadEndBlocksAnalysis(const DeadEndBlocksAnalysis &) = delete;
DeadEndBlocksAnalysis &operator=(const DeadEndBlocksAnalysis &) = delete;

static bool classof(const SILAnalysis *s) {
return s->getKind() == SILAnalysisKind::DeadEndBlocks;
}

std::unique_ptr<DeadEndBlocks> newFunctionAnalysis(SILFunction *f) override {
return std::make_unique<DeadEndBlocks>(f);
}

bool shouldInvalidate(SILAnalysis::InvalidationKind k) override {
return k & InvalidationKind::Branches;
}

protected:
void verify(DeadEndBlocks *deBlocks) const override;
};

} // namespace swift

#endif
12 changes: 6 additions & 6 deletions lib/SIL/Utils/BasicBlockUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,22 +363,22 @@ void swift::mergeBasicBlockWithSingleSuccessor(SILBasicBlock *BB,
//===----------------------------------------------------------------------===//

void DeadEndBlocks::compute() {
assert(ReachableBlocks.empty() && "Computed twice");
assert(reachableBlocks.empty() && "Computed twice");

// First step: find blocks which end up in a no-return block (terminated by
// an unreachable instruction).
// Search for function-exiting blocks, i.e. return and throw.
for (const SILBasicBlock &BB : *F) {
for (const SILBasicBlock &BB : *f) {
const TermInst *TI = BB.getTerminator();
if (TI->isFunctionExiting())
ReachableBlocks.insert(&BB);
reachableBlocks.insert(&BB);
}
// Propagate the reachability up the control flow graph.
unsigned Idx = 0;
while (Idx < ReachableBlocks.size()) {
const SILBasicBlock *BB = ReachableBlocks[Idx++];
while (Idx < reachableBlocks.size()) {
const SILBasicBlock *BB = reachableBlocks[Idx++];
for (SILBasicBlock *Pred : BB->getPredecessorBlocks())
ReachableBlocks.insert(Pred);
reachableBlocks.insert(Pred);
}
}

Expand Down
3 changes: 2 additions & 1 deletion lib/SILOptimizer/Analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ target_sources(swiftSILOptimizer PRIVATE
ClassHierarchyAnalysis.cpp
ClosureScope.cpp
ColdBlockInfo.cpp
DifferentiableActivityAnalysis.cpp
DeadEndBlocksAnalysis.cpp
DestructorAnalysis.cpp
DifferentiableActivityAnalysis.cpp
EscapeAnalysis.cpp
EpilogueARCAnalysis.cpp
FunctionOrder.cpp
Expand Down
57 changes: 57 additions & 0 deletions lib/SILOptimizer/Analysis/DeadEndBlocksAnalysis.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//===--- DeadEndBlocksAnalysis.cpp ----------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
#include "swift/AST/Decl.h"
#include "swift/SIL/SILFunction.h"

using namespace swift;

void DeadEndBlocksAnalysis::verify(DeadEndBlocks *deBlocks) const {
// If the passed in deBlocks has not computed, there is nothing to check.
if (!deBlocks->isComputed())
return;

// Then create our new dead end blocks instance so we can check the internal
// state of our input against it.
auto *fn = deBlocks->getFunction();
DeadEndBlocks newBlocks(fn);

// Make sure that all values that deBlocks thinks is unreachable are
// actually unreachable.
//
// NOTE: We verify like this b/c DeadEndBlocks looks up state lazily so we
// can only check the work we have done so far.
for (auto &block : *fn) {
if (deBlocks->isDeadEnd(&block)) {
if (!newBlocks.isDeadEnd(&block)) {
llvm::errs() << "DeadEndBlocksAnalysis Error! Found dead end block "
"that is no longer a dead end block?!";
llvm_unreachable("standard error assertion");
}
} else {
if (newBlocks.isDeadEnd(&block)) {
llvm::errs() << "DeadEndBlocksAnalysis Error! Found reachable block "
"that is no longer reachable?!";
llvm_unreachable("standard error assertion");
}
}
}
}

//===----------------------------------------------------------------------===//
// Main Entry Point
//===----------------------------------------------------------------------===//

SILAnalysis *swift::createDeadEndBlocksAnalysis(SILModule *) {
return new DeadEndBlocksAnalysis();
}
13 changes: 5 additions & 8 deletions lib/SILOptimizer/SemanticARC/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace semanticarc {
struct LLVM_LIBRARY_VISIBILITY Context {
SILFunction &fn;
ARCTransformKind transformKind = ARCTransformKind::All;
Optional<DeadEndBlocks> deadEndBlocks;
DeadEndBlocks &deadEndBlocks;
ValueLifetimeAnalysis::Frontier lifetimeFrontier;
SmallMultiMapCache<SILValue, Operand *> addressToExhaustiveWriteListCache;

Expand Down Expand Up @@ -117,14 +117,11 @@ struct LLVM_LIBRARY_VISIBILITY Context {
using FrozenMultiMapRange =
decltype(joinedOwnedIntroducerToConsumedOperands)::PairToSecondEltRange;

DeadEndBlocks &getDeadEndBlocks() {
if (!deadEndBlocks)
deadEndBlocks.emplace(&fn);
return *deadEndBlocks;
}
DeadEndBlocks &getDeadEndBlocks() { return deadEndBlocks; }

Context(SILFunction &fn, bool onlyGuaranteedOpts, InstModCallbacks callbacks)
: fn(fn), deadEndBlocks(), lifetimeFrontier(),
Context(SILFunction &fn, DeadEndBlocks &deBlocks, bool onlyGuaranteedOpts,
InstModCallbacks callbacks)
: fn(fn), deadEndBlocks(deBlocks), lifetimeFrontier(),
addressToExhaustiveWriteListCache(constructCacheValue),
onlyGuaranteedOpts(onlyGuaranteedOpts), instModCallbacks(callbacks) {}

Expand Down
5 changes: 3 additions & 2 deletions lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor

Context ctx;

explicit SemanticARCOptVisitor(SILFunction &fn, bool onlyGuaranteedOpts)
: ctx(fn, onlyGuaranteedOpts,
explicit SemanticARCOptVisitor(SILFunction &fn, DeadEndBlocks &deBlocks,
bool onlyGuaranteedOpts)
: ctx(fn, deBlocks, onlyGuaranteedOpts,
InstModCallbacks(
[this](SILInstruction *inst) { eraseInstruction(inst); },
[this](Operand *use, SILValue newValue) {
Expand Down
6 changes: 5 additions & 1 deletion lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "Transforms.h"

#include "swift/Basic/Defer.h"
#include "swift/SILOptimizer/Analysis/Analysis.h"
#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"

#include "llvm/Support/CommandLine.h"
Expand Down Expand Up @@ -149,7 +151,9 @@ struct SemanticARCOpts : SILFunctionTransform {
"Can not perform semantic arc optimization unless ownership "
"verification is enabled");

SemanticARCOptVisitor visitor(f, guaranteedOptsOnly);
auto *deBlocksAnalysis = getAnalysis<DeadEndBlocksAnalysis>();
SemanticARCOptVisitor visitor(f, *deBlocksAnalysis->get(&f),
guaranteedOptsOnly);

#ifndef NDEBUG
// If we are being asked for testing purposes to run a series of transforms
Expand Down