@@ -49,6 +49,7 @@ sources = [
'passes/PrepareForTcgPass/PrepareForTcgPass.cpp',
'passes/PrepareForTcgPass/TransformGEPs.cpp',
'passes/PrepareForTcgPass/CanonicalizeIR.cpp',
+ 'passes/PrepareForTcgPass/IdentityMap.cpp',
]
clang = bindir / 'clang'
new file mode 100644
@@ -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();
+ }
+}
new file mode 100644
@@ -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);
@@ -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();
}
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