Files
revng-revng/lib/RestructureCFG/InlineDivergentScopesPass.cpp
Pietro Fezzardi 48749974f0 IDS: simplifyTerminator doesn't merge default
With this commit, the `simplifyTerminator` helper function used by IDS
stops trying to merge the default cases with one of the other cases
arbitrarily.
Now, if the `UnreachableSuccessor` was reached via the default case, the
default case is still arbitrarily redirected to an arbitrary case
successor, but the case and the default do not get merged.
This keeps the logic of the transform simpler, and also allows for
easier successive manipulation by planned upcoming transforms.
2025-11-06 13:32:56 +01:00

677 lines
27 KiB
C++

//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/GenericDomTree.h"
#include "revng/ADT/Concepts.h"
#include "revng/RestructureCFG/InlineDivergentScopesPass.h"
#include "revng/RestructureCFG/ScopeGraphAlgorithms.h"
#include "revng/RestructureCFG/ScopeGraphGraphTraits.h"
#include "revng/RestructureCFG/ScopeGraphUtils.h"
#include "revng/Support/Assert.h"
#include "revng/Support/Debug.h"
#include "revng/Support/GraphAlgorithms.h"
#include "revng/Support/IRBuilder.h"
#include "revng/Support/IRHelpers.h"
using namespace llvm;
/// Manager class to automatically handle the creation and clean up of the
/// `PlaceHolderTarget`
class PlaceHolderTargetManager {
private:
BasicBlock *PlaceHolderTarget;
public:
PlaceHolderTargetManager(Function &F) {
LLVMContext &Context = getContext(&F);
PlaceHolderTarget = BasicBlock::Create(Context,
"placeholder_destination",
&F);
}
~PlaceHolderTargetManager() { PlaceHolderTarget->eraseFromParent(); }
BasicBlock *operator*() const { return PlaceHolderTarget; }
};
static bool isConditional(BasicBlock *Node) {
auto Successors = children<Scope<BasicBlock *>>(Node);
size_t NumSuccessors = std::distance(Successors.begin(), Successors.end());
if (NumSuccessors >= 2) {
return true;
} else {
return false;
}
}
/// Helper function which detects if `Node` is an exit for the `ScopeGraph`
static bool isExit(BasicBlock *Node) {
auto Successors = children<Scope<BasicBlock *>>(Node);
return std::ranges::empty(Successors);
}
/// Helper function to retrieve the target BasicBlock of a `GotoBlock`
static BasicBlock *getGotoTarget(const BasicBlock *BB) {
revng_assert(isGotoBlock(BB));
const Instruction *Terminator = BB->getTerminator();
const BranchInst *Branch = llvm::cast<BranchInst>(Terminator);
revng_assert(Branch->isUnconditional());
return Branch->getSuccessor(0);
}
/// Helper function to collect all the exit nodes on the `ScopeGraph`
static SmallVector<BasicBlock *> getExits(Function &F,
BasicBlock *PlaceHolderTarget) {
SmallVector<BasicBlock *> TrueExits;
SmallVector<BasicBlock *> GotoExits;
Scope<Function *> ScopeGraph(&F);
for (BasicBlock *BB : nodes(ScopeGraph)) {
// We need to exclude the `PlaceHolderTarget` which is a temporary block
// which incidentally happens to be an exit, but will be removed before the
// end of the pass
if (BB != PlaceHolderTarget and isExit(BB)) {
if (not isGotoBlock(BB)) {
TrueExits.push_back(BB);
} else {
GotoExits.push_back(BB);
}
}
}
// We now order the `GotoExits` by placing first the ones whose `goto` target
// comes first in `post_order` in the graph. This criterion should maximize
// the opportunities of performing `MaterializeTrivialGoto` transformations.
// Create a map to store the `post_order` indexes, that we can later reuse
// when performing the comparison
llvm::DenseMap<const BasicBlock *, unsigned> POIndexes;
// Fill the map
unsigned Index = 0;
for (BasicBlock *BB : post_order(ScopeGraph)) {
POIndexes[BB] = Index;
Index++;
}
// Sort the `GotoExits` according to the above criterion
std::sort(GotoExits.begin(),
GotoExits.end(),
[&POIndexes](const BasicBlock *A, const BasicBlock *B) {
return POIndexes[getGotoTarget(A)] < POIndexes[getGotoTarget(B)];
});
// Merge the `TrueExits` with the `GotoExits`
GotoExits.append(TrueExits);
return GotoExits;
}
/// Helper to obtain the immediate dominator `BasicBlock`, if present
static BasicBlock *
getImmediateDominator(BasicBlock *N,
DomTreeOnView<BasicBlock, Scope> &DomTree) {
auto *Node = DomTree.getNode(N)->getIDom();
if (Node) {
return Node->getBlock();
} else {
return nullptr;
}
}
/// Helper function which collects all the nodes reachable from a vector of
/// nodes (`Successors`)
static MapVector<BasicBlock *, SmallSet<BasicBlock *, 4>>
getReachablesFromSuccessor(BasicBlock *N) {
using ScopeBlock = Scope<BasicBlock *>;
SetVector<BasicBlock *> Successors;
for (BasicBlock *Successor : children<ScopeBlock>(N)) {
Successors.insert(Successor);
}
MapVector<BasicBlock *, SmallSet<BasicBlock *, 4>> ReachablesFromSuccessor;
for (auto *Successor : Successors) {
SmallSet<BasicBlock *, 4> ReachableExits;
// Explore all the exits that are reachable from each Successor.
// For having a divergent exit, we need to find a set of
// successors that reach only the exit under analysis
auto &Reachables = ReachablesFromSuccessor[Successor];
for (auto *DFSNode : depth_first(Scope<BasicBlock *>(Successor))) {
if (isExit(DFSNode)) {
Reachables.insert(DFSNode);
}
}
}
return ReachablesFromSuccessor;
}
/// We define a `Descriptor` for the information needed to identify a
/// `Divergence`. Specifically, the divergence is composed by the conditional
/// where divergence originates, a set of divergent successors, and by the exit
/// node which is divergent wrt. those divergent successors.
struct DivergenceDescriptor {
BasicBlock *Conditional;
std::set<BasicBlock *> DivergentSuccessors;
BasicBlock *Exit;
};
/// Helper function that tries to identify a `DivergenceDescriptor`
static std::optional<DivergenceDescriptor>
electDivergence(BasicBlock *Candidate,
BasicBlock *Exit,
DomTreeOnView<BasicBlock, Scope> &DomTree) {
// Check if it is the conditional making it a divergent node
if (isConditional(Candidate)) {
revng_assert(DomTree.dominates(Candidate, Exit));
MapVector<BasicBlock *, SmallSet<BasicBlock *, 4>>
ReachablesFromSuccessor = getReachablesFromSuccessor(Candidate);
size_t SuccessorsSize = ReachablesFromSuccessor.size();
std::set<BasicBlock *> DivergentSuccessors;
for (const auto &[Successor, ReachableExits] : ReachablesFromSuccessor) {
if (ReachableExits.size() == 1 and *ReachableExits.begin() == Exit) {
DivergentSuccessors.insert(Successor);
} else if (ReachableExits.contains(Exit)) {
// If we reach `Exit` from a successor which is not a candidate for
// being divergent, it means that `Exit` is reached also by a non
// divergent successor, and this contradicts the definition, so we
// cannot find a divergence here, and we return a `nullopt`
return std::nullopt;
}
}
// We proceed only if there are some divergent exits and some
// non divergent exits, it doesn't make sense to transform a situation where
// all the edges are all divergent or all non-divergent
if (DivergentSuccessors.size() < SuccessorsSize) {
// We employ this `std::optional` as return value, in order to signal if
// we identified a candidate for IDS
return DivergenceDescriptor{ Candidate, DivergentSuccessors, Exit };
}
}
// When no divergence was found, we signal it by returning `nullopt`
return std::nullopt;
}
/// Simplifies the terminator of `BB` treating `UnreachableSuccessor` as
/// unreachable. `UnreachableSuccessor` must be a successor of `BB`.
static void simplifyTerminator(llvm::BasicBlock *BB,
const llvm::BasicBlock *UnreachableSuccessor) {
revng_assert(llvm::any_of(llvm::successors(BB),
[UnreachableSuccessor](const llvm::BasicBlock *B) {
return B == UnreachableSuccessor;
}));
Instruction *Terminator = BB->getTerminator();
if (auto *Branch = dyn_cast<BranchInst>(Terminator)) {
if (Branch->isConditional()) {
// We want to transform a conditional branch with one of the destination
// set to `UnreachableSuccessor` to a non conditional branch
BasicBlock *SingleDestination = nullptr;
if (Branch->getSuccessor(0) == UnreachableSuccessor) {
SingleDestination = Branch->getSuccessor(1);
revng_assert(SingleDestination != UnreachableSuccessor);
} else if (Branch->getSuccessor(1) == UnreachableSuccessor) {
SingleDestination = Branch->getSuccessor(0);
revng_assert(SingleDestination != UnreachableSuccessor);
}
// If we found a `BranchInst` candidate for promotion, we substitute it
// with an unconditional branch
if (SingleDestination) {
// TODO: checks are only omitted here because of unit tests.
revng::NonDebugInfoCheckingIRBuilder Builder(Terminator);
// We set the debug metadata of the promoted `Branch` instruction to the
// same value it has before the promotion is performed
Builder.CreateBr(SingleDestination);
// We remove the old conditional branch
Terminator->eraseFromParent();
}
}
} else if (auto *Switch = dyn_cast<SwitchInst>(Terminator)) {
// Handle the simplification for non-default cases jumping to
// `UnreachableSuccessor`.
for (auto CaseIt = Switch->case_begin(); CaseIt != Switch->case_end();) {
if (CaseIt->getCaseSuccessor() == UnreachableSuccessor) {
// We do not want to have a situation where the `UnreachableSuccessor`
// is both the `default` successor of a `switch` and one of its standard
// case
CaseIt = Switch->removeCase(CaseIt);
} else {
++CaseIt;
}
}
// Handle the simplification when `UnreachableSuccessor` is the default.
BasicBlock *DefaultTarget = Switch->getDefaultDest();
if (DefaultTarget == UnreachableSuccessor) {
// It should never be the case that we end up with a `switch` having only
// `UnreachableSuccessor` as its default successor.
revng_assert(Switch->getNumCases() != 0);
// We redirect the default to point to the same case of the first case.
// This is arbitrary, but the contract of this function is that
// `UnreachableSuccessor` should be guaranteed to be unreachable, so we
// can redirect any path that would reach it to wherever we want.
Switch->setDefaultDest(Switch->case_begin()->getCaseSuccessor());
}
}
}
/// Helper function which create a single entry point block where a divergence
/// is entered into by multiple successors of a `Conditional`
static void createHead(BasicBlock *Conditional,
DivergenceDescriptor &Divergence,
BasicBlock *PlaceHolderTarget) {
LLVMContext &Context = getContext(Conditional);
Function *F = Conditional->getParent();
Instruction *ConditionalTerminator = Conditional->getTerminator();
BasicBlock *Head = BasicBlock::Create(Context,
Conditional->getName() + "_head_ids",
F);
Instruction *HeadTerminator = ConditionalTerminator->clone();
// TODO: the checks should be enabled conditionally based on the user.
revng::NonDebugInfoCheckingIRBuilder HeadBuilder(Head);
HeadBuilder.Insert(HeadTerminator);
// Collect the `Successor`s composing the local `Divergence`
std::set<BasicBlock *> LocalDivergentSuccessors = Divergence
.DivergentSuccessors;
// And collect all the `Successor`s that do not make up the local
// `Divergence`
SmallVector<BasicBlock *> LocalNonDivergentSuccessors;
for (BasicBlock *Successor : children<Scope<BasicBlock *>>(Head)) {
if (not LocalDivergentSuccessors.contains(Successor)) {
LocalNonDivergentSuccessors.push_back(Successor);
}
}
// The `Head` only connects the `LocalDivergentSuccessors`, we simplify
// away the other successors
replaceSuccessors(HeadTerminator,
LocalNonDivergentSuccessors,
PlaceHolderTarget);
simplifyTerminator(Head, PlaceHolderTarget);
// If we insert the `Head`, we replace all the edges going to the
// `DivergentSuccessors` in the conditional so that they go to `Head`
replaceSuccessors(ConditionalTerminator, LocalDivergentSuccessors, Head);
}
/// Helper function that performs the IDS transformation
static void
performMultipleIDS(const ScopeGraphBuilder &SGBuilder,
SmallVector<DivergenceDescriptor> &MultipleDivergences,
BasicBlock *PlaceHolderTarget) {
// Create the new `BasicBlock` representing the `C'` conditional
// inserted by the IDS transformation. We will refer to this block as the
// `Tail` (of the IDS group of nodes).
BasicBlock *Conditional = MultipleDivergences[0].Conditional;
LLVMContext &Context = getContext(Conditional);
Function *F = Conditional->getParent();
BasicBlock *Tail = BasicBlock::Create(Context,
Conditional->getName() + "_ids",
F);
revng_assert(Tail->empty());
// We clone the terminator already present in `BasicBlock` `Conditional`, so
// that a superset of the correct final successors are already connected to
// `Tail`.
Instruction *ConditionalTerminator = Conditional->getTerminator();
Instruction *TailTerminator = ConditionalTerminator->clone();
// TODO: we shouldn't be losing debug information here!
revng::NonDebugInfoCheckingIRBuilder TailBuilder(Tail);
TailBuilder.Insert(TailTerminator);
// Populate the `DivergentSuccessors` summing all ones present in each
// `DivergenceDescriptor`
std::set<BasicBlock *> DivergentSuccessors;
for (auto Divergence : MultipleDivergences) {
DivergentSuccessors.insert(Divergence.DivergentSuccessors.begin(),
Divergence.DivergentSuccessors.end());
}
// We connect the `Conditional` to `Tail`, by making sure that all
// the previous slots and cases going to the nondivergent exits, are now
// connected to the `Tail` block, in order to preserve the original semantics.
// The original paths going to the nondivergent exits, are preserved by the
// the fact that the `Terminator` has been cloned into `Tail`.
SmallVector<BasicBlock *> NonDivergentSuccessors;
for (BasicBlock *Successor : children<Scope<BasicBlock *>>(Conditional)) {
if (not DivergentSuccessors.contains(Successor)) {
NonDivergentSuccessors.push_back(Successor);
}
}
revng_assert(not DivergentSuccessors.empty());
// It may be that for a specific `Conditional` we elected all the exits as
// divergent. In this case, we need to arbitrarily elect one exit as the `one
// true exit`, and we do this by electing the last element in
// `MultipleDivergences`.
if (NonDivergentSuccessors.empty()) {
revng_assert(MultipleDivergences.size() > 1);
// The fact that we elect the last element in `MultipleDivergences` as the
// only non divergent exit, is very important, due to how the `exit` blocks
// are enqueued in `MupltipleDivergences`. If present, the `Goto` exit will
// be in the first positions, followed by the original exit blocks. In this
// way, if present, we always elect a original exit as the non divergent
// exit.
size_t LastElementIndex = MultipleDivergences.size() - 1;
// The first element in `MultipleDivergencies` is arbitrarily elected as the
// component not going to a divergent exit
NonDivergentSuccessors
.append(MultipleDivergences[LastElementIndex].DivergentSuccessors.begin(),
MultipleDivergences[LastElementIndex].DivergentSuccessors.end());
// We remove the `Successor`s from the `DivergentSuccessors`
for (BasicBlock *Successor :
MultipleDivergences[LastElementIndex].DivergentSuccessors) {
DivergentSuccessors.erase(Successor);
}
// We erase the first `DivergenceDescriptor` from the `MultipleDivergences`
MultipleDivergences.erase(MultipleDivergences.begin() + LastElementIndex);
}
// Decide if we need a new `Head` that collects the entry of the divergent
// scope. This is needed if one `Divergence` is composed by multiple
// `Successor`s, since in that case there is no clear entry to the inlined
// divergent scope. We therefore add a `Head` block that collects the entry
// point, so that this new node will dominate the eventual postdominator
// common to the multiple `Successor`s.
for (auto Divergence : MultipleDivergences) {
if (Divergence.DivergentSuccessors.size() > 1) {
createHead(Conditional, Divergence, PlaceHolderTarget);
}
}
// Move the `NonDivergentSuccessors` outgoing edges from `Conditional` so that
// they point to `Tail`
replaceSuccessors(ConditionalTerminator, NonDivergentSuccessors, Tail);
// We remove from the `Terminator` of `Tail`, all the edges that
// target `DivergentSuccessor`, since it will be only reached by
// `Conditional`. We do this in two steps, we first substitute the original
// successor with `PlaceHolderTarget`, and then we invoke
// `simplifyTerminator`, which will take care of simplifying away the
// unnecessary successors, both for `brcond`s and `switch`es.
replaceSuccessors(TailTerminator, DivergentSuccessors, PlaceHolderTarget);
simplifyTerminator(Tail, PlaceHolderTarget);
// We add a `scope_closer` edge between the divergent exit node and
// the `Tail` node for all the exits
for (auto Divergence : MultipleDivergences) {
SGBuilder.addScopeCloser(Divergence.Exit, Tail);
}
}
/// Helper function which returns if a `DivergenceDescriptor` is compatible with
/// all the ones already present in a `Collection`. Being compatible means that
/// they insist on the same conditional, have non-overlapping `Exit` blocks, and
/// do not have any overlapping `DivergentSuccessors`. We currently use this
/// helper only in an assertion to double check that the `Divergencies`
/// collected respect the compatibility criterion.
static bool isCompatible(const SmallVector<DivergenceDescriptor> &Collection,
const DivergenceDescriptor &NewDescriptor) {
// A new element is always compatible with an empty `Collection`
if (Collection.empty())
return true;
// A new element is compatible when it shares the `Conditional` with the ones
// already in the `Collection`
if (Collection[0].Conditional != NewDescriptor.Conditional)
return false;
// All the `DivergenceDescriptor`s involving a certain `Conditional`, must
// partition, so that there is no overlap: 1: the divergent `Exit` reached by
// each `DivergenceDescriptor` 2: the set of the `DivergentSuccessors`
for (const auto &Existing : Collection) {
if (Existing.Exit == NewDescriptor.Exit) {
return false;
}
for (BasicBlock *ExistingSucc : Existing.DivergentSuccessors) {
for (BasicBlock *NewSucc : NewDescriptor.DivergentSuccessors) {
if (ExistingSucc == NewSucc) {
return false;
}
}
}
}
return true;
}
/// Helper function which attempts to perform multiple IDS transformations
static void
tryMultipleIDS(const ScopeGraphBuilder &SGBuilder,
const SmallVector<DivergenceDescriptor> &DivergenceDescriptors,
BasicBlock *PlaceHolderTarget) {
// We attempt to perform multiple IDS transformations all at once
// We start by collecting all the `Conditional`s involved in at least one
// divergence
SmallSetVector<BasicBlock *, 4> Conditionals;
for (auto DivergenceDescriptor : DivergenceDescriptors) {
Conditionals.insert(DivergenceDescriptor.Conditional);
}
// For each `Conditional`, we collect all the multiple divergences involving
// it
for (BasicBlock *Conditional : Conditionals) {
// Support variables used to understand when we need to restart the
// collection
// of the `DivergentDescriptor`s
bool AllExitGotos = true;
size_t CoveredSuccessors = 0;
size_t ConditionalSuccessors = getScopeGraphSuccessors(Conditional).size();
SmallVector<DivergenceDescriptor> CompatibleDivergenceDescriptors;
for (auto DivergenceDescriptor : DivergenceDescriptors) {
if (DivergenceDescriptor.Conditional == Conditional) {
// As a safeguard, we assert that all the `DivergenceDescriptor`s are
// compatible, i.e., they partition correctly the divergencies insisting
// on a certain `Conditional`
revng_assert(isCompatible(CompatibleDivergenceDescriptors,
DivergenceDescriptor));
CompatibleDivergenceDescriptors.push_back(DivergenceDescriptor);
// Update the `AllExitGotos` state with the information of the current
// `Exit`
AllExitGotos = AllExitGotos and isGotoBlock(DivergenceDescriptor.Exit);
// Update the `CoveredSuccessors` information with the current
// divergence
CoveredSuccessors += DivergenceDescriptor.DivergentSuccessors.size();
}
}
// We must find at least one `DivergenceDescriptor` for each `Conditional`,
// since we previously selected only `Conditional`s involved in at least
// one `Divergence`
revng_assert(CompatibleDivergenceDescriptors.size() >= 1);
performMultipleIDS(SGBuilder,
CompatibleDivergenceDescriptors,
PlaceHolderTarget);
// Criterion which restarts the collection and processing of the divergent
// exits before every already collected divergence is processed.
// This must happen when:
// 1) The `Conditional` has only divergent successors.
// 2) All the divergent successors lead to a `goto` exit.
// The reasoning is the following: if a `Conditional` has all `goto`
// divergent successors, once we perform IDS for such `Conditional` one of
// the successors will be elected as the non divergent one, and the
// corresponding exit node, which is a `goto` exit, may become a new
// divergent exit for another `Conditional` upper in the `ScopeGraph`. In
// such case, if we do not restart the collection of the divergent exits, we
// may process divergences already collected, before the newly introduced
// one. And this violates the invariant that we always process divergent
// `goto` exits before the non `goto` exits. And this in turn may cause
// suboptimalities when `IDS` is combined with `MaterializeTrivialGoto` in
// order to reduce the number of emitted `goto`s.
if (AllExitGotos and CoveredSuccessors == ConditionalSuccessors)
return;
}
}
/// This helper function is used to attempt the IDS process, and returns `true`
/// or `false` depending on whether a change is performed
static bool tryIDS(const ScopeGraphBuilder &SGBuilder,
Function &F,
BasicBlock *PlaceHolderTarget) {
// The main need for recomputing the `DomTree` is that we insert the new
// `IDS` block and redirect edges over the `ScopeGraph`
DomTreeOnView<BasicBlock, Scope> DomTree;
DomTree.recalculate(F);
SmallVector<DivergenceDescriptor> DivergenceDescriptors;
// Collect the exit nodes
SmallVector<BasicBlock *> Exits = getExits(F, PlaceHolderTarget);
// Collect all the IDS opportunities.
// We iterate over all the `Exit`s, and then search for a `Candidate`
// conditional node for which such `Exit` is divergent wrt. We do this by
// walking upward the dominator tree until we find (if it is present) the
// divergent `Conditional`. An alternative, which would also make the code
// more straightforward, would be to iterate over the `Conditional`s and start
// the search from there, but this would mean to perform multiple visits to
// the `Exit`s. With this techniques instead, even if less intuitive, we
// collect all the `DivergenceDescriptor`s in one sweep.
for (BasicBlock *Exit : Exits) {
// Here we go up in the `ScopeGraph`, hopping through the immediate
// dominators of the `Exit` block with the goal of finding the divergent
// conditional
BasicBlock *Candidate = Exit;
while ((Candidate = getImmediateDominator(Candidate, DomTree))) {
// We use the following `std::optional` in order to contain the
// divergent node candidate, with all the object needed to perform the
// transformation
std::optional<DivergenceDescriptor>
DivergenceDescriptor = electDivergence(Candidate, Exit, DomTree);
// If we have found a divergence for the exit under analysis, we add it to
// the `DivergenceDescriptors`
if (DivergenceDescriptor) {
DivergenceDescriptors.push_back(*DivergenceDescriptor);
// We have found a `Divergence` for the current `Exit`, so we can move
// to the next one
break;
}
}
}
// If we did not collect any `DivergenceDescriptor`, we return `false` in
// order to signal that no change was performed at the last round
if (DivergenceDescriptors.empty()) {
return false;
} else {
tryMultipleIDS(SGBuilder, DivergenceDescriptors, PlaceHolderTarget);
return true;
}
}
/// Implementation class used to run the `IDS` transformation
class InlineDivergentScopesImpl {
Function &F;
const ScopeGraphBuilder SGBuilder;
public:
InlineDivergentScopesImpl(Function &F) : F(F), SGBuilder(&F) {}
public:
bool run() {
// We keep a boolean variable to track whether the `Function` was modified
bool FunctionModified = false;
// Manager object for the creation and deletion of the `PlaceHolderTarget`
PlaceHolderTargetManager PlaceHolderTarget(F);
// Every time we perform a change due to the IDS restructuring, we may have
// unlocked the potential to perform new IDS closures in nested subtree
// where the last modification was performed, therefore we need to retry
// IDS. Empirically, we have found that restarting the analysis, by
// recollecting the exit nodes in the `ScopeGraph`, is faster than
// continuing with the processing of all the exits already collected.
// Therefore, we should not really try to optimize this, unless we find new
// evidence that this is better.
while (tryIDS(SGBuilder, F, *PlaceHolderTarget)) {
// As soon as one IDS change is performed, we mark the current `Function`
// as modified
FunctionModified = true;
}
return FunctionModified;
}
};
char InlineDivergentScopesPass::ID = 0;
static constexpr const char *Flag = "inline-divergent-scopes";
using Reg = llvm::RegisterPass<InlineDivergentScopesPass>;
static Reg X(Flag,
"Perform the inline of divergent scopes canonicalization process");
bool InlineDivergentScopesPass::runOnFunction(llvm::Function &F) {
// Instantiate and call the `Impl` class
InlineDivergentScopesImpl IDSImpl(F);
bool FunctionModified = IDSImpl.run();
// We verify that the `ScopeGraph` has not blocks disconnected from the
// entry block
if (VerifyLog.isEnabled()) {
revng_assert(not hasUnreachableBlocks(&F));
}
// This pass may transform the CFG by assign some blocks to perform the IDS
// canonicalization and by redirecting edges on the `ScopeGraph`
return FunctionModified;
}
void InlineDivergentScopesPass::getAnalysisUsage(llvm::AnalysisUsage &AU)
const {
// This pass does not preserve the CFG
}