MaterializeLoopScopes: drop pass

Drop the `MaterializeLoopScopes` pass after the re-design the loop
matching stage to work entirely on `clift`, without the need to insert a
`scope_closer` edge to guide the emission.

Simultaneously, drop the DAGify `Head` metadata insertion, since it will
not be checked anymore during the `MaterializeLoopScopes` pass.
This commit is contained in:
Andrea Gussoni
2025-09-25 14:07:31 +02:00
parent 92e8ec3982
commit 855a9309ec
7 changed files with 2 additions and 649 deletions
@@ -1,20 +0,0 @@
#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
class MaterializeLoopScopes : public llvm::FunctionPass {
public:
static char ID;
public:
MaterializeLoopScopes() : llvm::FunctionPass(ID) {}
bool runOnFunction(llvm::Function &F) override;
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override;
};
-1
View File
@@ -21,7 +21,6 @@ revng_add_analyses_library_internal(
GenericRegionPass.cpp
InlineDispatcherSwitch.cpp
InlineDivergentScopesPass.cpp
MaterializeLoopScopes.cpp
MaterializeTrivialGotoPass.cpp
MetaRegion.cpp
PromoteCallNoReturn.cpp
+1 -17
View File
@@ -21,19 +21,6 @@ using namespace llvm;
// Debug logger
static Logger<> Log("dagify");
/// Helper function used to insert on the `Head` node the metadata that will be
/// later checked for consistency
static void insertHeadMD(BasicBlock *Head) {
// We attach a metadata to the terminator instruction of the `Head`
// block. Later on, we will check during `MaterializeLoopScopes` that
// the `Head` block of each `GenericRegion` will be the same
Instruction *HeadTerminator = Head->getTerminator();
QuickMetadata QMD(getContext(HeadTerminator));
auto *HeadMD = QMD.tuple();
HeadTerminator->setMetadata("genericregion-head", HeadMD);
}
class DAGifyPassImpl {
Function &F;
const ScopeGraphBuilder SGBuilder;
@@ -155,12 +142,9 @@ public:
RegionNodes.insert(RegionNode);
}
// Mark the `Head` block with the custom named metadata
// 1. Process the retreating edges of the `GenericRegion`
BasicBlock *Head = Region->getHead();
revng_assert(Head);
insertHeadMD(Head);
// 1. Process the retreating edges of the `GenericRegion`
using GT = GraphTraits<BasicBlock *>;
auto Retreatings = getBackedgesWhiteList<BasicBlock *, GT>(Head,
RegionNodes);
@@ -1,395 +0,0 @@
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <optional>
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/GenericDomTree.h"
#include "revng/RestructureCFG/GenericRegionInfo.h"
#include "revng/RestructureCFG/MaterializeLoopScopes.h"
#include "revng/RestructureCFG/ScopeGraphAlgorithms.h"
#include "revng/RestructureCFG/ScopeGraphGraphTraits.h"
#include "revng/RestructureCFG/ScopeGraphUtils.h"
#include "revng/Support/Assert.h"
using namespace llvm;
// Debug logger
static Logger<> Log("materialize-loop-scopes");
/// Helper function which inserts the `scope_closer` edge representing the
/// `MaterializeLoopScope` operation result
static void addScopeCloser(const ScopeGraphBuilder &SGBuilder,
BasicBlock *Head,
BasicBlock *UniqueSuccessor) {
// We split the `Head` block in order to insert a `scope_start`, so that we
// can insert the `scope_closer` between `scope_start` and the elected
// `UniqueSuccessor` materializing the `scope` corresponding to the loop body
Instruction *HeadTerminator = Head->getTerminator();
BasicBlock *LoopStart = Head->splitBasicBlock(HeadTerminator, "loop_start");
// Log the `UniqueSuccessor` to which we are inserting the `scope_closer` to
revng_log(Log,
"The elected unique successor is block:"
<< UniqueSuccessor->getName() << "\n");
// We insert the `scope_closer` edge from the `Head` to the `UniqueSuccessor`
SGBuilder.addScopeCloser(Head, UniqueSuccessor);
}
/// Helper function used to verify that the elected `Head` contains the metadata
/// attached during the `DAGify` pass
static void verifyHeadMD(BasicBlock *Head) {
// We check that each `Head` block has a metadata attached during the
// `DAGify` pass
Instruction *HeadTerminator = Head->getTerminator();
auto *MD = HeadTerminator->getMetadata("genericregion-head");
// We must find the metadata by design
revng_assert(MD);
auto *Tuple = dyn_cast_or_null<MDTuple>(MD);
revng_assert(Tuple);
}
/// Helper to obtain the immediate postdominator `BasicBlock`, if present
static BasicBlock *
getImmediatePostDominator(BasicBlock *N,
PostDomTreeOnView<BasicBlock, Scope> &PostDomTree) {
auto *Node = PostDomTree.getNode(N)->getIDom();
if (Node) {
return Node->getBlock();
} else {
return nullptr;
}
}
/// Helper function to populate the sets of exiting and successor candidates
static void
collectExitBlocks(GenericRegion<BasicBlock *> *Region,
std::set<BasicBlock *> &ExitingBlocks,
std::set<BasicBlock *> &UniqueSuccessorCandidates) {
for (auto *RegionNode : Region->blocks()) {
SmallSetVector<BasicBlock *, 2>
Successors = getScopeGraphSuccessors(RegionNode);
for (BasicBlock *Successor : Successors) {
if (not Region->containsBlock(Successor)) {
// We collect the `Successor`s of the `GenericRegion`
UniqueSuccessorCandidates.insert(Successor);
// We collect all the blocks from which there is a exiting edge
// from the `GenericRegion`
ExitingBlocks.insert(RegionNode);
}
}
}
}
/// Helper function used to create the `Footer` block, which will become the
/// unique successor of the `GenericRegion`, and to divert the exiting edges to
/// such block
static BasicBlock *
enforceFooterSuccessor(GenericRegion<BasicBlock *> *Region,
std::set<BasicBlock *> &ExitingBlocks,
std::set<BasicBlock *> &UniqueSuccessorCandidates) {
revng_assert(ExitingBlocks.size() == 1);
BasicBlock *UniqueExitingBlock = *ExitingBlocks.begin();
llvm::SmallSet<BasicBlock *, 4> InternalSuccessors;
SmallSetVector<BasicBlock *, 2>
Successors = getScopeGraphSuccessors(UniqueExitingBlock);
for (BasicBlock *Successor : Successors) {
if (Region->containsBlock(Successor)) {
InternalSuccessors.insert(Successor);
}
}
auto *ExitingBlockTerminator = UniqueExitingBlock->getTerminator();
// We clone the original `Terminator` in the `footer` block
LLVMContext &Context = getContext(UniqueExitingBlock);
Function *F = UniqueExitingBlock->getParent();
BasicBlock *Footer = BasicBlock::Create(Context, "footer", F);
Instruction *FooterTerminator = ExitingBlockTerminator->clone();
IRBuilder<> FooterBuilder(Footer);
FooterBuilder.Insert(FooterTerminator);
// We divert the edges exiting from the `GenericRegion` to the
// `Footer` block
replaceSuccessors(ExitingBlockTerminator, UniqueSuccessorCandidates, Footer);
// In the `Footer`, we need to remove all the edges not going to the
// `GenericRegion` successors
for (BasicBlock *InternalSuccessor : InternalSuccessors) {
simplifyTerminator(Footer, InternalSuccessor);
}
return Footer;
}
/// Helper function used to check for the preconditions needed on the
/// predecessors of the candidate immediate post dominator `UniqueSuccessor`.
/// Specifically, we check that all the predecessors are either in the current
/// `GenericRegion` or in the parent one.
static bool
checkPredecessorsCondition(GenericRegion<BasicBlock *> *Region,
GenericRegion<BasicBlock *> *ParentRegion,
BasicBlock *UniqueSuccessor) {
// Check that `UniqueSuccessor` belong to `ParentRegion`
if (not ParentRegion->containsBlock(UniqueSuccessor)) {
// Log the motivation for the exclusion of the `UniqueSuccessor`
// candidate
revng_log(Log,
"Candidate unique successor " << UniqueSuccessor->getName()
<< " is not in the parent region, "
"and needs to be "
"discarded");
// If the identified `UniqueSuccessor` is not in the parent
// `GenericRegion`, we signal this fact
return false;
}
// Check that all the predecessors of the candidate `UniqueSuccessor`
// are either in the `GenericRegion` under analysis or in its parent
// `GenericRegion`
SmallSetVector<BasicBlock *, 2>
Predecessors = getScopeGraphPredecessors(UniqueSuccessor);
for (BasicBlock *Predecessor : Predecessors) {
if ((not Region->containsBlock(Predecessor))
or (not ParentRegion->containsBlock(Predecessor))) {
// Log the motivation for the exclusion of the `UniqueSuccessor`
// candidate
revng_log(Log,
"Candidate unique successor " << UniqueSuccessor->getName()
<< " has not compatible "
"predecessors, and needs to "
"be discarded");
return false;
}
}
return true;
}
/// Helper function used to check for the preconditions needed on the
/// predecessors of the candidate immediate post dominator `UniqueSuccessor`.
/// Specifically, we check that all the predecessors are dominated by the elect
/// entry of the `GenericRegion` (the `Head`).
static bool checkPredecessorsDominance(BasicBlock *Head,
BasicBlock *UniqueSuccessor) {
DomTreeOnView<BasicBlock, Scope> DomTree;
Function *F = Head->getParent();
DomTree.recalculate(*F);
// Check that all the predecessors are either dominated by the entry
// node of the loop
SmallSetVector<BasicBlock *, 2>
Predecessors = getScopeGraphPredecessors(UniqueSuccessor);
for (BasicBlock *Predecessor : Predecessors) {
if (not DomTree.dominates(Head, Predecessor)) {
// Log the motivation for the exclusion of the `UniqueSuccessor`
// candidate
revng_log(Log,
"Candidate unique successor " << UniqueSuccessor->getName()
<< " has not compatible "
"predecessors, and needs to "
"be discarded");
return false;
}
}
return true;
}
static std::optional<BasicBlock *>
tryEnforceUniqueSuccessor(GenericRegion<BasicBlock *> *Region) {
// Collect the exiting blocks and the `SuccessorCandidates` of the
// `GenericRegion`
std::set<BasicBlock *> ExitingBlocks;
std::set<BasicBlock *> UniqueSuccessorCandidates;
collectExitBlocks(Region, ExitingBlocks, UniqueSuccessorCandidates);
std::optional<BasicBlock *> UniqueSuccessor;
// A. We iterate over all the successors blocks of each node in the
// `GenericRegion`. If there is a unique candidate successor, we can
// elect such block as `UniqueSuccessor`..
// If the loop has a single clearly identified successor, we can proceed
// with the insertion of the `scope_closer` to the `UniqueSuccessor`
if (UniqueSuccessorCandidates.size() == 1) {
UniqueSuccessor = *UniqueSuccessorCandidates.begin();
return UniqueSuccessor;
}
// B. If the `GenericRegion` has a single block with exiting edges, we
// insert a `footer` block grouping such exiting edges, and we insert
// a `scope_closer` to it
if (ExitingBlocks.size() == 1 and UniqueSuccessorCandidates.size() > 1) {
BasicBlock *Footer = enforceFooterSuccessor(Region,
ExitingBlocks,
UniqueSuccessorCandidates);
return Footer;
}
// C. If we did not elect the `UniqueSuccessor` in the previous stages,
// we try to navigate up in the post dominator tree until we find the
// first immediate post dominator block which is outside the current
// `GenericRegion.
revng_assert(not UniqueSuccessor);
BasicBlock *Head = Region->getHead();
Function *F = Head->getParent();
PostDomTreeOnView<BasicBlock, Scope> PostDomTree;
PostDomTree.recalculate(*F);
BasicBlock *Candidate = Head;
while ((Candidate = getImmediatePostDominator(Candidate, PostDomTree))) {
if (not Region->containsBlock(Candidate)) {
// Here we have identified the first node outside the
// `GenericRegion` which postdominates the entry node. This node
// will be our candidate for becoming the exit node of the
// `GenericRegion`.
UniqueSuccessor = Candidate;
break;
}
}
// We did not find a `UniqueSuccessor`
if (not UniqueSuccessor) {
return std::nullopt;
}
// We admit the election of a immediate post dominator block as the
// `UniqueSuccessor` of a `GenericRegion` only if both the following
// properties hold:
// 1: The candidate block must be in the direct parent of the
// `GenericRegion`.
// 2: All the incoming edges into the `UniqueSuccessor` block
// originate either from blocks in the `GenericRegion` under
// analysis (edges exiting the `GenericRegion`) or in its parent.
auto *ParentRegion = Region->getParent();
// We may not have a `ParentRegion`, in that case the `GenericRegion` we
// are analyzing is at the first level in the `RegionTree`, and the
// following check is not needed
if (ParentRegion) {
// If the preconditions are not satisfied, we cannot identify the
// `UniqueSuccessor`
if (not checkPredecessorsCondition(Region,
ParentRegion,
*UniqueSuccessor)) {
return std::nullopt;
}
}
// We now check that all the predecessors of the candidate immediate
// post dominator block `UniqueSuccessor` are dominated by the `Head`,
// which is another precondition for the election of the
// `UniqueSuccessor` of the `GenericRegion`
if (not checkPredecessorsDominance(Head, *UniqueSuccessor)) {
return std::nullopt;
}
// If we reach this point, it means that all the preconditions are
// satisfied, and that we can elect the `UniqueSuccessor`
return UniqueSuccessor;
}
/// Implementation class used to run the `MaterializeLoopScopes`
/// transformation
class MaterializeLoopScopesImpl {
Function &F;
ScopeGraphBuilder SGBuilder;
public:
MaterializeLoopScopesImpl(Function &F) : F(F), SGBuilder(&F) {}
public:
bool run() {
// We instantiate and run the `GenericRegionInfo` analysis on the raw CFG,
// and not on the `ScopeGraph`. This is done because the current pass runs
// after `DAGify`, which disrupts the loops on the `ScopeGraph`.
// Under the assumption that the successor order of each `BasicBlock` is not
// modified between `DAGify` and `MaterializeLoopScopes`, we have the
// guarantee that the `GenericRegionInfo` computed remains equivalent.
GenericRegionInfo<Function *> RegionInfo;
RegionInfo.compute(&F);
// We keep a boolean variable to track whether the `Function` was modified
bool FunctionModified = false;
// We iterate over all the `GenericRegion`s that were found
for (auto &TopLevelRegion : RegionInfo.top_level_regions()) {
for (auto *Region : post_order(&TopLevelRegion)) {
// Retrieve the elected `Head` of the `GenericRegion`
BasicBlock *Head = Region->getHead();
revng_log(Log, "Elected head is: " << Head->getName() << "\n");
// Verify that the `Head` is the same one elected during the `DAGify`
// pass
verifyHeadMD(Head);
// 1. Try to find a `UniqueSuccessor` candidate
std::optional<BasicBlock *>
UniqueSuccessor = tryEnforceUniqueSuccessor(Region);
// 2. Enforce the single successor, if found, by adding a `scope_closer`
// to it, for the `GenericRegion`
if (UniqueSuccessor) {
// We mark the current `Function` as modified
FunctionModified = true;
// We insert the `scope_closer` to `UniqueSuccessor`
addScopeCloser(SGBuilder, Head, *UniqueSuccessor);
}
}
}
return FunctionModified;
}
};
char MaterializeLoopScopes::ID = 0;
static constexpr const char *Flag = "materialize-loop-scopes";
using Reg = llvm::RegisterPass<MaterializeLoopScopes>;
static Reg X(Flag, "Perform the materialization of loop scopes transformation");
bool MaterializeLoopScopes::runOnFunction(llvm::Function &F) {
// Instantiate and call the `Impl` class
MaterializeLoopScopesImpl MLoopScopesImpl(F);
bool FunctionModified = MLoopScopesImpl.run();
// This pass may transform the CFG by transforming some edges into `goto`
// edges, and by adding some `scope_closer` edges on the `ScopeGraph`
return FunctionModified;
}
void MaterializeLoopScopes::getAnalysisUsage(llvm::AnalysisUsage &AU) const {
// This pass does not preserve the CFG
}
@@ -686,7 +686,6 @@ Branches:
- inline-divergent-scopes
- enforce-single-exit
- materialize-trivial-goto
- materialize-loop-scopes
- select-scope
- inline-divergent-scopes
- enforce-single-exit
+1 -1
View File
@@ -302,7 +302,7 @@ block_h:
; CHECK: block_a:
; CHECK-NEXT: br i1 %a, label %block_b, label %block_h
; CHECK: block_b:
; CHECK-NEXT: br label %block_c, !genericregion-head !2
; CHECK-NEXT: br label %block_c
; CHECK: block_c:
; CHECK-NEXT: br i1 %a, label %block_d, label %block_e
; CHECK: block_d:
@@ -1,214 +0,0 @@
;
; This file is distributed under the MIT License. See LICENSE.md for details.
;
; RUN: %revngopt %s -materialize-loop-scopes -S -o - | FileCheck %s
; function tags metadata needed for all the tests
declare !revng.tags !0 void @scope_closer(ptr)
declare !revng.tags !1 void @goto_block()
!0 = !{!"marker", !"scope-closer"}
!1 = !{!"marker", !"goto-block"}
!2 = !{}
; MLoopScopes on not dagified loop with single successor
define void @f(i1 noundef %a) {
block_a:
br label %block_b
block_b:
br label %block_c, !genericregion-head !2
block_c:
br label %block_d
block_d:
br i1 %a, label %block_b, label %block_e
block_e:
ret void
}
; CHECK-LABEL: define void @f(i1 noundef %a)
; CHECK: block_a:
; CHECK-NEXT: br label %block_b
; CHECK: block_b:
; CHECK-NEXT: call void @scope_closer(ptr blockaddress(@f, %block_e))
; CHECK-NEXT: br label %loop_start
; CHECK: loop_start:
; CHECK-NEXT: br label %block_c, !genericregion-head !2
; CHECK: block_c:
; CHECK-NEXT: br label %block_d
; CHECK: block_d:
; CHECK-NEXT: br i1 %a, label %block_b, label %block_e
; CHECK: block_e:
; CHECK-NEXT: ret void
; MLoopScopes on dagified loop with single successor
define void @g(i1 noundef %a) {
block_a:
br label %block_b
block_b:
br label %block_c, !genericregion-head !2
block_c:
br label %block_d
block_d:
br i1 %a, label %goto_block_b, label %block_e
goto_block_b:
call void @scope_closer(ptr blockaddress(@g, %block_e))
call void @goto_block()
br label %block_b
block_e:
ret void
}
; CHECK-LABEL: define void @g(i1 noundef %a)
; CHECK: block_a:
; CHECK-NEXT: br label %block_b
; CHECK: block_b:
; CHECK-NEXT: call void @scope_closer(ptr blockaddress(@g, %block_e))
; CHECK-NEXT: br label %loop_start
; CHECK: loop_start:
; CHECK-NEXT: br label %block_c, !genericregion-head !2
; CHECK: block_c:
; CHECK-NEXT: br label %block_d
; CHECK: block_d:
; CHECK-NEXT: br i1 %a, label %goto_block_b, label %block_e
; CHECK: goto_block_b:
; CHECK-NEXT: call void @scope_closer(ptr blockaddress(@g, %block_e))
; CHECK-NEXT: call void @goto_block()
; CHECK-NEXT: br label %block_b
; CHECK: block_e:
; CHECK-NEXT: ret void
; MLoopScopes on not dagified loop with multiple successors, footer insertion
define void @h(i32 noundef %a) {
block_a:
br label %block_b
block_b:
switch i32 %a, label %block_e [
i32 0, label %block_f
i32 1, label %block_c
], !genericregion-head !2
block_c:
br label %block_d
block_d:
br label %block_b
block_e:
br label %block_g
block_f:
br label %block_g
block_g:
ret void
}
; CHECK-LABEL: define void @h(i32 noundef %a)
; CHECK: block_a:
; CHECK-NEXT: br label %block_b
; CHECK: block_b:
; CHECK-NEXT: call void @scope_closer(ptr blockaddress(@h, %footer))
; CHECK-NEXT: br label %loop_start
; CHECK: loop_start:
; CHECK-NEXT: switch i32 %a, label %footer [
; CHECK-NEXT: i32 0, label %footer
; CHECK-NEXT: i32 1, label %block_c
; CHECK-NEXT: ], !genericregion-head !2
; CHECK: block_c:
; CHECK-NEXT: br label %block_d
; CHECK: block_d:
; CHECK-NEXT: br label %block_b
; CHECK: block_e:
; CHECK-NEXT: br label %block_g
; CHECK: block_f:
; CHECK-NEXT: br label %block_g
; CHECK: block_g:
; CHECK-NEXT: ret void
; CHECK: footer:
; CHECK-NEXT: switch i32 %a, label %block_e [
; CHECK-NEXT: i32 0, label %block_f
; CHECK-NEXT: ], !genericregion-head !2
; MLoopScopes on dagified loop with multiple successors, no footer is inserted
define void @i(i32 noundef %a) {
block_a:
br label %block_b
block_b:
switch i32 %a, label %block_b_ids [
i32 0, label %block_b_ids
i32 1, label %block_c
], !genericregion-head !2
block_c:
br label %block_d
block_d:
br label %goto_block_b
block_e:
br label %block_g
block_f:
br label %block_g
block_g:
ret void
goto_block_b:
call void @goto_block()
call void @scope_closer(ptr blockaddress(@i, %block_b_ids))
br label %block_b
block_b_ids:
switch i32 %a, label %block_e [
i32 0, label %block_f
]
}
; CHECK-LABEL: define void @i(i32 noundef %a)
; CHECK: block_a:
; CHECK-NEXT: br label %block_b
; CHECK: block_b:
; CHECK-NEXT: call void @scope_closer(ptr blockaddress(@i, %block_b_ids))
; CHECK-NEXT: br label %loop_start
; CHECK: loop_start:
; CHECK-NEXT: switch i32 %a, label %block_b_ids [
; CHECK-NEXT: i32 0, label %block_b_ids
; CHECK-NEXT: i32 1, label %block_c
; CHECK-NEXT: ], !genericregion-head !2
; CHECK: block_c:
; CHECK-NEXT: br label %block_d
; CHECK: block_d:
; CHECK-NEXT: br label %goto_block_b
; CHECK: block_e:
; CHECK-NEXT: br label %block_g
; CHECK: block_f:
; CHECK-NEXT: br label %block_g
; CHECK: block_g:
; CHECK-NEXT: ret void
; CHECK: goto_block_b:
; CHECK-NEXT: call void @goto_block()
; CHECK-NEXT: call void @scope_closer(ptr blockaddress(@i, %block_b_ids))
; CHECK-NEXT: br label %block_b
; CHECK: block_b_ids:
; CHECK-NEXT: switch i32 %a, label %block_e [
; CHECK-NEXT: i32 0, label %block_f
; CHECK-NEXT: ]