mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Mostly functionally neutral: - may fix latent bugs. - may reduce useless basic blocks after inlining. This rewrite encapsulates the cloner's internal state, providing a clean API for the CRTP subclasses. The subclasses are rewritten to use the exposed API and extension points. This makes it much easier to understand, work with, and extend SIL cloners, which are central to many optimization passes. Basic SIL invariants are now clearly expressed and enforced. There is no longer a intricate dance between multiple levels of subclasses operating on underlying low-level data structures. All of the logic needed to keep the original SIL in a consistent state is contained within the SILCloner itself. Subclasses only need to be responsible for their own modifications. The immediate motiviation is to make CFG updates self-contained so that SIL remains in a valid state. This will allow the removal of critical edge splitting hacks and will allow general SIL utilities to take advantage of the fact that we don't allow critical edges. This rewrite establishes a simple principal that should be followed everywhere: aside from the primitive mutation APIs on SIL data types, each SIL utility is responsibile for leaving SIL in a valid state and the logic for doing so should exist in one central location. This includes, for example: - Generating a valid CFG, splitting edges if needed. - Returning a valid instruction iterator if any instructions are removed. - Updating dominance. - Updating SSA (block arguments). (Dominance info and SSA properties are fundamental to SIL verification). LoopInfo is also somewhat fundamental to SIL, and should generally be updated, but it isn't required. This also fixes some latent bugs related to iterator invalidation in recursivelyDeleteTriviallyDeadInstructions and SILInliner. Note that the SILModule deletion callback should be avoided. It can be useful as a simple cache invalidation mechanism, but it is otherwise bug prone, too limited to be very useful, and basically bad design. Utilities that mutate should return a valid instruction iterator and provide their own deletion callbacks.
216 lines
6.9 KiB
C++
216 lines
6.9 KiB
C++
//===--- DebugUtils.h - Utilities for debug-info instructions ---*- 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains utilities to work with debug-info related instructions:
|
|
// debug_value and debug_value_addr.
|
|
//
|
|
// SIL optimizations should deal with debug-info related instructions when
|
|
// looking at the uses of a value.
|
|
// When performing an analysis, the usual thing is to just ignore all debug-info
|
|
// instructions.
|
|
// When transforming the SIL, a pass must decide what to do with debug-info
|
|
// instructions. Either delete them (if their value is no longer available),
|
|
// keep them (if the transformation has no effect on debug-info values) or
|
|
// update them.
|
|
//
|
|
// To ignore debug-info instructions during an analysis, this file provides
|
|
// some utility functions, which can be used instead of the relevant member
|
|
// functions in ValueBase and SILValue:
|
|
//
|
|
// V->use_empty() -> onlyHaveDebugUses(V)
|
|
// V.hasOneUse() -> hasOneNonDebugUse(V)
|
|
// V.getUses() -> getNonDebugUses(V)
|
|
// I->eraseFromParent() -> eraseFromParentWithDebugInsts(I)
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SIL_DEBUGUTILS_H
|
|
#define SWIFT_SIL_DEBUGUTILS_H
|
|
|
|
#include "swift/SIL/SILBasicBlock.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
|
|
namespace swift {
|
|
|
|
class SILInstruction;
|
|
|
|
/// Deletes all of the debug instructions that use \p Inst.
|
|
inline void deleteAllDebugUses(ValueBase *Inst) {
|
|
for (auto UI = Inst->use_begin(), E = Inst->use_end(); UI != E;) {
|
|
auto *Inst = UI->getUser();
|
|
UI++;
|
|
if (Inst->isDebugInstruction())
|
|
Inst->eraseFromParent();
|
|
}
|
|
}
|
|
|
|
/// This iterator filters out any debug (or non-debug) instructions from a range
|
|
/// of uses, provided by the underlying ValueBaseUseIterator.
|
|
/// If \p nonDebugInsts is true, then the iterator provides a view to all non-
|
|
/// debug instructions. Otherwise it provides a view ot all debug-instructions.
|
|
template <bool nonDebugInsts> class DebugUseIterator
|
|
: public std::iterator<std::forward_iterator_tag, Operand *, ptrdiff_t> {
|
|
|
|
ValueBaseUseIterator BaseIterator;
|
|
|
|
// Skip any debug or non-debug instructions (depending on the nonDebugInsts
|
|
// template argument).
|
|
void skipInsts() {
|
|
while (true) {
|
|
if (*BaseIterator == nullptr)
|
|
return;
|
|
|
|
SILInstruction *User = BaseIterator->getUser();
|
|
if (User->isDebugInstruction() != nonDebugInsts)
|
|
return;
|
|
|
|
BaseIterator++;
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
DebugUseIterator(ValueBaseUseIterator BaseIterator) :
|
|
BaseIterator(BaseIterator) {
|
|
skipInsts();
|
|
}
|
|
|
|
DebugUseIterator() = default;
|
|
|
|
Operand *operator*() const { return *BaseIterator; }
|
|
Operand *operator->() const { return *BaseIterator; }
|
|
SILInstruction *getUser() const { return BaseIterator.getUser(); }
|
|
|
|
DebugUseIterator &operator++() {
|
|
BaseIterator++;
|
|
skipInsts();
|
|
return *this;
|
|
}
|
|
|
|
DebugUseIterator operator++(int unused) {
|
|
DebugUseIterator Copy = *this;
|
|
++*this;
|
|
return Copy;
|
|
}
|
|
friend bool operator==(DebugUseIterator lhs,
|
|
DebugUseIterator rhs) {
|
|
return lhs.BaseIterator == rhs.BaseIterator;
|
|
}
|
|
friend bool operator!=(DebugUseIterator lhs,
|
|
DebugUseIterator rhs) {
|
|
return !(lhs == rhs);
|
|
}
|
|
};
|
|
|
|
/// Iterator for iteration over debug instructions.
|
|
using DUIterator = DebugUseIterator<false>;
|
|
|
|
/// Iterator for iteration over non-debug instructions.
|
|
using NonDUIterator = DebugUseIterator<true>;
|
|
|
|
|
|
/// Returns a range of all debug instructions in the uses of a value (e.g.
|
|
/// SILValue or SILInstruction).
|
|
inline iterator_range<DUIterator> getDebugUses(SILValue V) {
|
|
return make_range(DUIterator(V->use_begin()), DUIterator(V->use_end()));
|
|
}
|
|
|
|
/// Returns a range of all non-debug instructions in the uses of a value (e.g.
|
|
/// SILValue or SILInstruction).
|
|
inline iterator_range<NonDUIterator> getNonDebugUses(SILValue V) {
|
|
return make_range(NonDUIterator(V->use_begin()), NonDUIterator(V->use_end()));
|
|
}
|
|
|
|
/// Returns true if a value (e.g. SILInstruction) has no uses except debug
|
|
/// instructions.
|
|
inline bool onlyHaveDebugUses(SILValue V) {
|
|
auto NonDebugUses = getNonDebugUses(V);
|
|
return NonDebugUses.begin() == NonDebugUses.end();
|
|
}
|
|
|
|
/// Return true if all of the results of the given instruction have no uses
|
|
/// except debug instructions.
|
|
inline bool onlyHaveDebugUsesOfAllResults(SILInstruction *I) {
|
|
for (auto result : I->getResults()) {
|
|
if (!onlyHaveDebugUses(result))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Returns true if a value (e.g. SILInstruction) has exactly one use which is
|
|
/// not a debug instruction.
|
|
inline bool hasOneNonDebugUse(SILValue V) {
|
|
auto Range = getNonDebugUses(V);
|
|
auto I = Range.begin(), E = Range.end();
|
|
if (I == E) return false;
|
|
return ++I == E;
|
|
}
|
|
|
|
// Returns the use if the value has only one non debug user.
|
|
inline SILInstruction *getSingleNonDebugUser(SILValue V) {
|
|
auto Range = getNonDebugUses(V);
|
|
auto I = Range.begin(), E = Range.end();
|
|
if (I == E) return nullptr;
|
|
if (std::next(I) != E)
|
|
return nullptr;
|
|
return I->getUser();
|
|
}
|
|
|
|
/// Erases the instruction \p I from it's parent block and deletes it, including
|
|
/// all debug instructions which use \p I.
|
|
/// Precondition: The instruction may only have debug instructions as uses.
|
|
/// If the iterator \p InstIter references any deleted instruction, it is
|
|
/// incremented.
|
|
inline void eraseFromParentWithDebugInsts(SILInstruction *I,
|
|
SILBasicBlock::iterator &InstIter) {
|
|
auto results = I->getResults();
|
|
|
|
bool foundAny;
|
|
do {
|
|
foundAny = false;
|
|
for (auto result : results) {
|
|
while (!result->use_empty()) {
|
|
foundAny = true;
|
|
auto *User = result->use_begin()->getUser();
|
|
assert(User->isDebugInstruction());
|
|
if (InstIter == User->getIterator())
|
|
InstIter++;
|
|
|
|
User->eraseFromParent();
|
|
}
|
|
}
|
|
} while (foundAny);
|
|
|
|
if (InstIter == I->getIterator())
|
|
++InstIter;
|
|
|
|
I->eraseFromParent();
|
|
}
|
|
|
|
/// Erases the instruction \p I from it's parent block and deletes it, including
|
|
/// all debug instructions which use \p I.
|
|
/// Precondition: The instruction may only have debug instructions as uses.
|
|
inline void eraseFromParentWithDebugInsts(SILInstruction *I) {
|
|
SILBasicBlock::iterator nullIter;
|
|
eraseFromParentWithDebugInsts(I, nullIter);
|
|
}
|
|
|
|
/// Return true if the def-use graph rooted at \p V contains any non-debug,
|
|
/// non-trivial users.
|
|
bool hasNonTrivialNonDebugTransitiveUsers(
|
|
PointerUnion<SILInstruction *, SILArgument *> V);
|
|
|
|
} // end namespace swift
|
|
|
|
#endif /* SWIFT_SIL_DEBUGUTILS_H */
|