Skip to content

[NFC] Add SSADestroyHoisting prototype (originally in CopyForwarding) #40392

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 9 commits into from
Dec 22, 2021
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
50 changes: 47 additions & 3 deletions include/swift/SIL/MemAccessUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,12 @@ inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) {
return !(a == SILAccessKind::Read && b == SILAccessKind::Read);
}

/// Return true if \p instruction is a deinitialization barrier.
///
/// Deinitialization barriers constrain variable lifetimes. Lexical end_borrow
/// and destroy_addr cannot be hoisted above them.
bool isDeinitBarrier(SILInstruction *instruction);

} // end namespace swift

//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1303,9 +1309,8 @@ struct AccessUseVisitor {
///
/// Return true if all uses were collected. This is always true as long the \p
/// visitor's visitUse method returns true.
bool visitAccessStorageUses(AccessUseVisitor &visitor,
AccessStorage storage,
SILFunction *function);
bool visitAccessStorageUses(AccessUseVisitor &visitor, AccessStorage storage,
SILFunction *function);

/// Visit the uses of \p accessPath.
///
Expand All @@ -1319,6 +1324,45 @@ bool visitAccessPathUses(AccessUseVisitor &visitor, AccessPath accessPath,

} // end namespace swift

//===----------------------------------------------------------------------===//
// MARK: UniqueAddressUses
//===----------------------------------------------------------------------===//

namespace swift {

/// Analyze and classify the leaf uses of unique storage.
///
/// Storage that has a unique set of roots within this function includes
/// alloc_stack, alloc_box, exclusive argument, and global variables. All access
/// to the storage within this function is derived from these roots.
///
/// Gather the kinds of uses that are typically relevant to algorithms:
/// - loads (including copies out of, not including inout args)
/// - stores (including copies into and inout args)
/// - destroys (of the entire aggregate)
/// - debugUses (only populated when preserveDebugInfo == false)
/// - unknownUses (e.g. address_to_pointer, box escape)
struct UniqueStorageUseVisitor {
static bool findUses(UniqueStorageUseVisitor &visitor);

SILFunction *function;
AccessStorage storage;

UniqueStorageUseVisitor(AccessStorage storage, SILFunction *function)
: function(function), storage(storage) {}

virtual ~UniqueStorageUseVisitor() = default;

virtual bool visitLoad(Operand *use) = 0;
virtual bool visitStore(Operand *use) = 0;
virtual bool visitDestroy(Operand *use) = 0;
virtual bool visitDealloc(Operand *use) = 0;
virtual bool visitDebugUse(Operand *use) = 0;
virtual bool visitUnknownUse(Operand *use) = 0;
};

} // namespace swift

//===----------------------------------------------------------------------===//
// MARK: Helper API for specific formal access patterns
//===----------------------------------------------------------------------===//
Expand Down
9 changes: 9 additions & 0 deletions include/swift/SIL/SILFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,15 @@ class SILFunction
OptMode = unsigned(mode);
}

/// True if debug information must be preserved (-Onone).
///
/// If this is false (-O), then the presence of debug info must not affect the
/// outcome of any transformations.
///
/// Typically used to determine whether a debug_value is a normal SSA use or
/// incidental use.
bool preserveDebugInfo() const;

PerformanceConstraints getPerfConstraints() const { return perfConstraints; }

void setPerfConstraints(PerformanceConstraints perfConstr) {
Expand Down
16 changes: 16 additions & 0 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,14 @@ class SILInstruction : public llvm::ilist_node<SILInstruction> {
locationStorage = loc.storage;
}

/// Return the next instruction or nullptr if this is the last instruction in
/// its block.
SILInstruction *getPreviousInstruction();

/// Return the previous instruction or nullptr if this is the first
/// instruction in its block.
SILInstruction *getNextInstruction();

/// This method unlinks 'self' from the containing basic block and deletes it.
void eraseFromParent();

Expand Down Expand Up @@ -1013,6 +1021,14 @@ class SingleValueInstruction : public SILInstruction, public ValueBase {
/// If this is an instruction which "defines" an opened archetype, it is
/// returned.
CanArchetypeType getOpenedArchetype() const;

SILInstruction *getPreviousInstruction() {
return SILInstruction::getPreviousInstruction();
}

SILInstruction *getNextInstruction() {
return SILInstruction::getNextInstruction();
}
};

struct SILNodeOffsetChecker {
Expand Down
129 changes: 129 additions & 0 deletions include/swift/SILOptimizer/Analysis/Reachability.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//===--- Reachability.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
//
//===----------------------------------------------------------------------===//
///
/// Reachability data flow analysis using a path-discovery worklist. For
/// efficient data flow propagation based on a single SSA value and its uses.
///
/// TODO: Add an optimistic data flow for more aggresive optimization:
/// - Add another set for blocks reachable by barriers
/// - Change the meet operation to a union
/// - Propagate past barriers up to the SSA def
/// - Iterate to a fix-point.
///
//===----------------------------------------------------------------------===//

#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_REACHABILITY_H
#define SWIFT_SILOPTIMIZER_ANALYSIS_REACHABILITY_H

#include "swift/SIL/BasicBlockDatastructures.h"
#include "swift/SIL/SILBasicBlock.h"

namespace swift {

/// Pessimistic, non-iterative data flow for analyzing backward reachability
/// from a set of last uses to their dominating def or nearest barrier.
///
/// Intended for frequently called utilities where minimizing the cost of data
/// flow is more important than analyzing reachability across loops. Expected to
/// visit very few blocks because barriers often occur close to a last use.
///
/// BlockReachability {
/// // True if the beginning of \p block is reachable.
/// // Typically a BasicBlockSet wrapper.
/// bool hasReachableBegin(SILBasicBlock *block);
///
/// // Mark the beginning of a block reachable. Only called once per block.
/// // Typically a BasicBlockSet wrapper.
/// boid markReachableBegin(SILBasicBlock *block);
///
/// // Mark the end of a block reachable. Only called once per block.
/// // Typically a BasicBlockSet wrapper.
/// void markReachableEnd(SILBasicBlock *block);
///
/// // Return true if \p inst is a barrier. Called once for each reachable
/// // instruction, assuming that each lastUse is itself a barrier.
/// // Used by the data flow client to perform additional book-keeping,
/// // such as recording debug_value instructions.
/// bool checkReachableBarrier(SILInstruction *inst);
/// };
template <typename BlockReachability>
class BackwardReachability {
SILFunction *function;
BlockReachability &reachableBlocks;
BasicBlockWorklist cfgWorklist;

public:
BackwardReachability(SILFunction *function,
BlockReachability &reachableBlocks)
: function(function), reachableBlocks(reachableBlocks),
cfgWorklist(function) {}

// Initialize data flow starting points before running solveBackward.
void initLastUse(SILInstruction *lastUsePoint) {
auto *lastUseBlock = lastUsePoint->getParent();
if (canReachBlockBegin(lastUsePoint)) {
pushPreds(lastUseBlock);
}
}

// Data flow "meet": interesection of successor reachability.
void solveBackward() {
while (SILBasicBlock *block = cfgWorklist.popAndForget()) {
if (!meetOverSuccessors(block))
continue;

reachableBlocks.markReachableEnd(block);

if (canReachBlockBegin(block->getTerminator())) {
pushPreds(block);
}
}
}

protected:
BackwardReachability(BackwardReachability const &) = delete;
BackwardReachability &operator=(BackwardReachability const &) = delete;

// Perform a "meet" over successor begin reachability.
// Return true if \p predecessor's end is pessimistically reachable.
//
// Meet:
// ReachableEnd(predecessor) := intersection(ReachableBegin, successors)
bool meetOverSuccessors(SILBasicBlock *block) {
return llvm::all_of(block->getSuccessorBlocks(), [this](auto *successor) {
return reachableBlocks.hasReachableBegin(successor);
});
}

// Local data flow. Computes the block's flow function.
bool canReachBlockBegin(SILInstruction *lastReachablePoint) {
do {
if (reachableBlocks.checkReachableBarrier(lastReachablePoint))
return false;
lastReachablePoint = lastReachablePoint->getPreviousInstruction();
} while (lastReachablePoint);
return true;
}

// Propagate global data flow from \p succBB to its predecessors.
void pushPreds(SILBasicBlock *succBB) {
reachableBlocks.markReachableBegin(succBB);

for (SILBasicBlock *predBB : succBB->getPredecessorBlocks()) {
cfgWorklist.pushIfNotVisited(predBB);
}
}
};

} // end namespace swift

#endif
2 changes: 2 additions & 0 deletions include/swift/SILOptimizer/PassManager/Passes.def
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ PASS(DefiniteInitialization, "definite-init",
"Definite Initialization for Diagnostics")
PASS(DestroyHoisting, "destroy-hoisting",
"Hoisting of value destroys")
PASS(SSADestroyHoisting, "ssa-destroy-hoisting",
"Hoist destroy_addr for uniquely identified values")
PASS(Devirtualizer, "devirtualizer",
"Indirect Call Devirtualization")
PASS(DiagnoseInfiniteRecursion, "diagnose-infinite-recursion",
Expand Down
4 changes: 4 additions & 0 deletions lib/SIL/IR/SILFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ OptimizationMode SILFunction::getEffectiveOptimizationMode() const {
return getModule().getOptions().OptMode;
}

bool SILFunction::preserveDebugInfo() const {
return getEffectiveOptimizationMode() <= OptimizationMode::NoOptimization;
}

bool SILFunction::shouldOptimize() const {
return getEffectiveOptimizationMode() != OptimizationMode::NoOptimization;
}
Expand Down
10 changes: 10 additions & 0 deletions lib/SIL/IR/SILInstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ SILModule &SILInstruction::getModule() const {
return getFunction()->getModule();
}

SILInstruction *SILInstruction::getPreviousInstruction() {
auto pos = getIterator();
return pos == getParent()->begin() ? nullptr : &*std::prev(pos);
}

SILInstruction *SILInstruction::getNextInstruction() {
auto nextPos = std::next(getIterator());
return nextPos == getParent()->end() ? nullptr : &*nextPos;
}

void SILInstruction::removeFromParent() {
#ifndef NDEBUG
for (auto result : getResults()) {
Expand Down
Loading