[PATCH v2 31/50] helper-to-tcg: TcgGenPass, linearize basic blocks

Anton Johansson via qemu development <[email protected]>
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
Passes over the IR that will follow all depend on a specific iteration
order over the IR that must match between passes, establish that
iteration order.  Also replace conditional branches with a pseudo
instruction closer in semantics to TCG, this breaks the IR verification,
but as this is the last transformation of the IR in this tool that's
acceptable.

Signed-off-by: Anton Johansson <[email protected]>
---
 subprojects/helper-to-tcg/meson.build         |   1 +
 .../src/TcgGenPass/LinearizeBlocks.cpp        | 140 ++++++++++++++++++
 .../src/TcgGenPass/LinearizeBlocks.hpp        |  32 ++++
 3 files changed, 173 insertions(+)
 create mode 100644 subprojects/helper-to-tcg/src/TcgGenPass/LinearizeBlocks.cpp
 create mode 100644 subprojects/helper-to-tcg/src/TcgGenPass/LinearizeBlocks.hpp

diff --git a/subprojects/helper-to-tcg/meson.build b/subprojects/helper-to-tcg/meson.build
index 880561416a..58e5f67153 100644
--- a/subprojects/helper-to-tcg/meson.build
+++ b/subprojects/helper-to-tcg/meson.build
@@ -49,6 +49,7 @@ sources = [
     'src/PrepareForTcgPass/CanonicalizeIR.cpp',
     'src/PrepareForTcgPass/IdentityMap.cpp',
     'src/TcgGenPass/TcgGenPass.cpp',
+    'src/TcgGenPass/LinearizeBlocks.cpp',
 ]
 
 clang = bindir / 'clang'
diff --git a/subprojects/helper-to-tcg/src/TcgGenPass/LinearizeBlocks.cpp b/subprojects/helper-to-tcg/src/TcgGenPass/LinearizeBlocks.cpp
new file mode 100644
index 0000000000..28dff1c572
--- /dev/null
+++ b/subprojects/helper-to-tcg/src/TcgGenPass/LinearizeBlocks.cpp
@@ -0,0 +1,140 @@
+//
+//  Copyright(c) 2026 rev.ng Labs Srl. All Rights Reserved.
+//
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "LinearizeBlocks.hpp"
+#include "PseudoInst.hpp"
+
+#include <llvm/ADT/PostOrderIterator.h>
+#include <llvm/ADT/SmallPtrSet.h>
+#include <llvm/ADT/SmallSet.h>
+#include <llvm/ADT/SmallVector.h>
+#include <llvm/Analysis/RegionInfo.h>
+#include <llvm/Analysis/VectorUtils.h>
+#include <llvm/IR/Constants.h>
+#include <llvm/IR/DerivedTypes.h>
+#include <llvm/IR/Dominators.h>
+#include <llvm/IR/Function.h>
+#include <llvm/IR/IRBuilder.h>
+#include <llvm/IR/InstIterator.h>
+#include <llvm/IR/InstrTypes.h>
+#include <llvm/IR/Instruction.h>
+#include <llvm/IR/Instructions.h>
+#include <llvm/IR/Intrinsics.h>
+#include <llvm/IR/Module.h>
+#include <llvm/IR/PassManager.h>
+#include <llvm/IR/PatternMatch.h>
+#include <llvm/Support/Casting.h>
+
+using namespace llvm;
+using namespace PatternMatch;
+
+// Needed to track and remove instructions not handled by a subsequent dead code
+// elimination, this applies to calls to pseudo instructions in particular.
+using EraseInstVec = SmallVector<Instruction *, 16>;
+
+static void convertICmpBrToPseudInst(LLVMContext &Context, RegionInfo &RI,
+                                     EraseInstVec &InstToErase, Module &M,
+                                     Instruction *I, BasicBlock *NextBB) {
+    auto *ICmp = dyn_cast<ICmpInst>(I);
+    if (!ICmp) {
+        return;
+    }
+
+    // Since we want to remove the icmp instruction we ensure that
+    // all uses are branch instructions that can be converted into
+    // @brcond.* calls.
+    for (User *U : ICmp->users()) {
+        if (!isa<BranchInst>(U)) {
+            return;
+        }
+    }
+
+    Value *Op0 = ICmp->getOperand(0);
+    Value *Op1 = ICmp->getOperand(1);
+    auto *CmpIntTy = dyn_cast<IntegerType>(Op0->getType());
+    if (!CmpIntTy) {
+        return;
+    }
+    for (User *U : ICmp->users()) {
+        auto *Br = cast<BranchInst>(U);
+
+        BasicBlock *True = Br->getSuccessor(0);
+        BasicBlock *False = Br->getSuccessor(1);
+
+        IRBuilder<> Builder(Br);
+
+        // TODO: This is strange, indeally the unreachable branch should have
+        // been optimized out, here we invert the conditional branch if we can
+        // fallthrough to the reachable branch.
+        bool TrueUnreachable =
+            True->getTerminator()->getOpcode() == Instruction::Unreachable and
+            False->getTerminator()->getOpcode() != Instruction::Unreachable;
+
+        // If the next basic block is either of our true/false
+        // branches, we can fallthrough instead of branching.
+        bool Fallthrough = (NextBB == True or NextBB == False);
+
+        // If the succeeding basic block is the true branch we
+        // invert the condition so we can fallthrough instead.
+        ICmpInst::Predicate Predicate;
+        if (NextBB == True or (TrueUnreachable and NextBB == False)) {
+            std::swap(True, False);
+            Predicate = ICmp->getInversePredicate();
+        } else {
+            Predicate = ICmp->getPredicate();
+        }
+
+        createPseudoInstCall(M, Builder, Brcond, Builder.getVoidTy(),
+                             {Builder.getInt8(Fallthrough),
+                              ConstantInt::get(CmpIntTy, Predicate), Op0, Op1,
+                              True, False});
+
+        InstToErase.push_back(Br);
+    }
+    InstToErase.push_back(ICmp);
+}
+
+LinearBlocks linearizeBlocks(Module &M, FunctionAnalysisManager &FAM,
+                             Function &F) {
+    assert(!F.isDeclaration());
+
+    LinearBlocks Blocks{};
+
+    LLVMContext &Context = F.getContext();
+    EraseInstVec InstToErase;
+    auto &RI = FAM.getResult<RegionInfoAnalysis>(F);
+
+    ReversePostOrderTraversal<Function *> RPOT(&F);
+    for (auto BBIt = RPOT.begin(); BBIt != RPOT.end(); ++BBIt) {
+        Blocks.push_back(*BBIt);
+    }
+
+    for (int i = 0; i < Blocks.size(); ++i) {
+        BasicBlock *BB = Blocks[i];
+        BasicBlock *NextBB = (i + 1 < Blocks.size()) ? Blocks[i + 1] : nullptr;
+        for (Instruction &I : *BB) {
+            convertICmpBrToPseudInst(Context, RI, InstToErase, M, &I, NextBB);
+        }
+    }
+
+    // Finally clean up instructions we need to remove manually
+    for (Instruction *I : InstToErase) {
+        I->eraseFromParent();
+    }
+
+    return Blocks;
+}
diff --git a/subprojects/helper-to-tcg/src/TcgGenPass/LinearizeBlocks.hpp b/subprojects/helper-to-tcg/src/TcgGenPass/LinearizeBlocks.hpp
new file mode 100644
index 0000000000..8d9a1afc54
--- /dev/null
+++ b/subprojects/helper-to-tcg/src/TcgGenPass/LinearizeBlocks.hpp
@@ -0,0 +1,32 @@
+//
+//  Copyright(c) 2026 rev.ng Labs Srl. All Rights Reserved.
+//
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, see <http://www.gnu.org/licenses/>.
+//
+
+#pragma once
+
+#include <llvm/ADT/SmallVector.h>
+#include <llvm/IR/PassManager.h>
+
+namespace llvm {
+class BasicBlock;
+class Function;
+class Module;
+} // namespace llvm
+
+using LinearBlocks = llvm::SmallVector<llvm::BasicBlock *, 16>;
+LinearBlocks linearizeBlocks(llvm::Module &M,
+                             llvm::FunctionAnalysisManager &FAM,
+                             llvm::Function &F);
-- 
2.52.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.