[RFC PATCH v1 27/43] helper-to-tcg: PrepareForTcgPass, identity map trivial expressions

Anton Johansson via posted 43 patches 2 days, 13 hours ago
[RFC PATCH v1 27/43] helper-to-tcg: PrepareForTcgPass, identity map trivial expressions
Posted by Anton Johansson via 2 days, 13 hours ago
Transformation of the IR, identity mapping trivial expressions which
would amount to nothing more than a move when emitted as TCG, but is
required in LLVM IR to not break the IR.

Trivial expressions are mapped to a @IdentityMap pseudo instruction
allowing them to be dealt with in a uniform manner down the line.

Signed-off-by: Anton Johansson <anjo@rev.ng>
---
 subprojects/helper-to-tcg/meson.build         |  1 +
 .../passes/PrepareForTcgPass/IdentityMap.cpp  | 80 +++++++++++++++++++
 .../passes/PrepareForTcgPass/IdentityMap.h    | 39 +++++++++
 .../PrepareForTcgPass/PrepareForTcgPass.cpp   |  4 +
 4 files changed, 124 insertions(+)
 create mode 100644 subprojects/helper-to-tcg/passes/PrepareForTcgPass/IdentityMap.cpp
 create mode 100644 subprojects/helper-to-tcg/passes/PrepareForTcgPass/IdentityMap.h

diff --git a/subprojects/helper-to-tcg/meson.build b/subprojects/helper-to-tcg/meson.build
index 50bb926f49..09caa74c63 100644
--- a/subprojects/helper-to-tcg/meson.build
+++ b/subprojects/helper-to-tcg/meson.build
@@ -49,6 +49,7 @@ sources = [
     'passes/PrepareForTcgPass/PrepareForTcgPass.cpp',
     'passes/PrepareForTcgPass/TransformGEPs.cpp',
     'passes/PrepareForTcgPass/CanonicalizeIR.cpp',
+    'passes/PrepareForTcgPass/IdentityMap.cpp',
 ]
 
 clang = bindir / 'clang'
diff --git a/subprojects/helper-to-tcg/passes/PrepareForTcgPass/IdentityMap.cpp b/subprojects/helper-to-tcg/passes/PrepareForTcgPass/IdentityMap.cpp
new file mode 100644
index 0000000000..b173aeba9c
--- /dev/null
+++ b/subprojects/helper-to-tcg/passes/PrepareForTcgPass/IdentityMap.cpp
@@ -0,0 +1,80 @@
+//
+//  Copyright(c) 2024 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 "IdentityMap.h"
+#include <PseudoInst.h>
+#include "backend/TcgType.h"
+#include <llvm/ADT/SmallVector.h>
+#include <llvm/IR/IRBuilder.h>
+#include <llvm/IR/InstIterator.h>
+#include <llvm/IR/Instruction.h>
+#include <llvm/IR/Value.h>
+
+using namespace llvm;
+
+void identityMap(Module &M, Function &F)
+{
+    SmallVector<Instruction *, 8> InstToErase;
+
+    for (auto &I : instructions(F)) {
+        auto *ZExt = dyn_cast<ZExtInst>(&I);
+        if (ZExt) {
+            auto *IntTy0 =
+                dyn_cast<IntegerType>(ZExt->getOperand(0)->getType());
+            auto *IntTy1 = dyn_cast<IntegerType>(ZExt->getType());
+            if (IntTy0 and IntTy1) {
+                uint32_t LlvmSize0 = IntTy0->getBitWidth();
+                uint32_t LlvmSize1 = IntTy1->getBitWidth();
+
+                if (LlvmSize0 == 1) {
+                    auto *ICmp = dyn_cast<ICmpInst>(ZExt->getOperand(0));
+                    if (ICmp) {
+                        auto *ICmpOp = ICmp->getOperand(0);
+                        LlvmSize0 =
+                            cast<IntegerType>(ICmpOp->getType())->getBitWidth();
+                    }
+                }
+
+                uint32_t TcgSize0 = llvmToTcgSize(LlvmSize0);
+                uint32_t TcgSize1 = llvmToTcgSize(LlvmSize1);
+
+                if (TcgSize0 == TcgSize1) {
+                    FunctionCallee Fn =
+                        pseudoInstFunction(M, IdentityMap, IntTy1, {IntTy0});
+                    IRBuilder<> Builder(&I);
+                    CallInst *Call =
+                        Builder.CreateCall(Fn, {ZExt->getOperand(0)});
+                    ZExt->replaceAllUsesWith(Call);
+                    InstToErase.push_back(&I);
+                }
+            }
+        } else if (isa<FreezeInst>(&I)) {
+            auto *IntTy0 = dyn_cast<IntegerType>(I.getOperand(0)->getType());
+            auto *IntTy1 = dyn_cast<IntegerType>(I.getType());
+            FunctionCallee Fn =
+                pseudoInstFunction(M, IdentityMap, IntTy1, {IntTy0});
+            IRBuilder<> Builder(&I);
+            CallInst *Call = Builder.CreateCall(Fn, {I.getOperand(0)});
+            I.replaceAllUsesWith(Call);
+            InstToErase.push_back(&I);
+        }
+    }
+
+    for (auto *I : InstToErase) {
+        I->eraseFromParent();
+    }
+}
diff --git a/subprojects/helper-to-tcg/passes/PrepareForTcgPass/IdentityMap.h b/subprojects/helper-to-tcg/passes/PrepareForTcgPass/IdentityMap.h
new file mode 100644
index 0000000000..b0c938c25d
--- /dev/null
+++ b/subprojects/helper-to-tcg/passes/PrepareForTcgPass/IdentityMap.h
@@ -0,0 +1,39 @@
+//
+//  Copyright(c) 2024 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/IR/Function.h>
+#include <llvm/IR/Module.h>
+
+//
+// Transformation of the IR, taking what would become trivial unary operations
+// and maps them to a single @IdentityMap pseudo instruction.
+//
+// To motivate further, in order to produce nice IR on the other end, generally
+// the operands of these trivial expressions needs to be forwarded and treated
+// as the destination value (identity mapped).  However, directly removing these
+// instructions will result in broken LLVM IR (consider zext i8, i32 where both
+// the source and destination would map to TCGv_i32).
+//
+// Moreover, handling these identity mapped values in an adhoc way quickly
+// becomes cumbersome and spreads throughout the codebase.  Therefore,
+// introducing @IdentityMap allows code further down the pipeline to ignore the
+// source of the identity map.
+//
+
+void identityMap(llvm::Module &M, llvm::Function &F);
diff --git a/subprojects/helper-to-tcg/passes/PrepareForTcgPass/PrepareForTcgPass.cpp b/subprojects/helper-to-tcg/passes/PrepareForTcgPass/PrepareForTcgPass.cpp
index 7fdbc2a0c9..3e4713d837 100644
--- a/subprojects/helper-to-tcg/passes/PrepareForTcgPass/PrepareForTcgPass.cpp
+++ b/subprojects/helper-to-tcg/passes/PrepareForTcgPass/PrepareForTcgPass.cpp
@@ -17,6 +17,7 @@
 
 #include "CanonicalizeIR.h"
 #include <CmdLineOptions.h>
+#include "IdentityMap.h"
 #include <PrepareForTcgPass.h>
 #include "TransformGEPs.h"
 #include <llvm/ADT/SCCIterator.h>
@@ -126,5 +127,8 @@ PreservedAnalyses PrepareForTcgPass::run(Module &M, ModuleAnalysisManager &MAM)
         transformGEPs(M, F, ResultTcgGlobalMap);
     }
     canonicalizeIR(M);
+    for (Function &F : M) {
+        identityMap(M, F);
+    }
     return PreservedAnalyses::none();
 }
-- 
2.45.2