[PATCH v2 08/50] helper-to-tcg: Handle LLVM version compatibility

Anton Johansson via qemu development <[email protected]>
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
Adds a translation unit with the sole purpose of handling inter-LLVM
code changes.  Instead of littering the code with #ifdefs, most of them
will be limited to LlvmCompat.[cpp|hpp] and a saner compat::*() function
is exposed in its place.

Signed-off-by: Anton Johansson <[email protected]>
---
 .../helper-to-tcg/include/LlvmCompat.hpp      | 124 ++++++++++++++++++
 subprojects/helper-to-tcg/meson.build         |   1 +
 subprojects/helper-to-tcg/src/LlvmCompat.cpp  |  89 +++++++++++++
 3 files changed, 214 insertions(+)
 create mode 100644 subprojects/helper-to-tcg/include/LlvmCompat.hpp
 create mode 100644 subprojects/helper-to-tcg/src/LlvmCompat.cpp

diff --git a/subprojects/helper-to-tcg/include/LlvmCompat.hpp b/subprojects/helper-to-tcg/include/LlvmCompat.hpp
new file mode 100644
index 0000000000..78822ca87b
--- /dev/null
+++ b/subprojects/helper-to-tcg/include/LlvmCompat.hpp
@@ -0,0 +1,124 @@
+//
+//  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
+
+//
+// The purpose of this file is to both collect and hide most api-specific
+// changes of LLVM [10,14]. Hopefully making it easier to keep track of the
+// changes necessary to support our targeted versions.
+//
+// Note some #ifdefs still remain throughout the codebase for larger codeblocks
+// that are specific enough such that pulling them here would be more cumbersome
+// than it's worth.
+//
+
+#include <llvm/Analysis/TargetTransformInfo.h>
+#include <llvm/IR/DerivedTypes.h>
+#include <llvm/IR/Intrinsics.h>
+#include <llvm/IR/Module.h>
+#include <llvm/IR/PassManager.h>
+#include <llvm/IR/PatternMatch.h>
+#include <llvm/MC/TargetRegistry.h>
+#include <llvm/Passes/OptimizationLevel.h>
+#include <llvm/Passes/PassBuilder.h>
+#include <llvm/Support/FileSystem.h>
+#include <llvm/Transforms/Utils/UnifyFunctionExitNodes.h>
+
+#include <stdint.h>
+
+namespace compat {
+
+constexpr auto OpenFlags = llvm::sys::fs::OF_TextWithCRLF;
+
+using OptimizationLevel = llvm::OptimizationLevel;
+
+constexpr auto LTOPhase = llvm::ThinOrFullLTOPhase::None;
+
+inline llvm::PassBuilder createPassBuilder(llvm::TargetMachine *TM,
+                                           llvm::PipelineTuningOptions &PTO) {
+#if LLVM_VERSION_MAJOR >= 16
+    return llvm::PassBuilder(TM, PTO, std::nullopt);
+#else
+    return llvm::PassBuilder(TM, PTO, llvm::None);
+#endif
+}
+
+// LLVM 16 changes the getFixedSize() -> getFixedValue()
+size_t getTypeAllocSize(const llvm::DataLayout &DL, llvm::Type *Ty);
+
+// These rely on string comparison with QEMU names using StringRef::startswith,
+// which changed to StringRef::starts_with in LLVM 18.
+bool isFunctionQemuHelper(llvm::StringRef Name);
+bool isFunctionQemuLoadStore(llvm::StringRef Name);
+
+// LLVM 21 moved to initializing a TargetTransformInfo via unique_ptr,
+// instead of by value.
+template <typename T>
+auto makeTTI(llvm::TargetMachine *TM, const llvm::Function &F) {
+    // Use explicit TargetTransformInfo() contructor
+#if LLVM_VERSION_MAJOR >= 21
+    return llvm::TargetTransformInfo(std::make_unique<T>(TM, F));
+#else
+    return llvm::TargetTransformInfo(T(TM, F));
+#endif
+}
+
+// Note, since constexpr auto is used to alias a function, default
+// arguments won't work.
+//
+// LLVM 20 deprecated getDeclaration() in favour of getOrInsertDeclaration()
+namespace Intrinsic {
+#if LLVM_VERSION_MAJOR >= 20
+constexpr auto getOrInsertDeclaration = llvm::Intrinsic::getOrInsertDeclaration;
+#else
+constexpr auto getOrInsertDeclaration = llvm::Intrinsic::getDeclaration;
+#endif
+} // namespace Intrinsic
+
+// Wrapper to convert Function- to Module analysis manager
+template <typename T>
+inline const typename T::Result *
+getModuleAnalysisManagerProxyResult(llvm::FunctionAnalysisManager &FAM,
+                                    llvm::Function &F) {
+    auto &MAMProxy = FAM.getResult<llvm::ModuleAnalysisManagerFunctionProxy>(F);
+    return MAMProxy.getCachedResult<T>(*F.getParent());
+}
+
+llvm::TargetMachine *getTargetMachine(llvm::Triple &TheTriple);
+
+//
+// LLVM 11 and below does not define the UnifyFunctionExitNodes pass
+// for the new pass manager.  Copy over the definition and use it for
+// 11 and below.
+//
+using llvm::UnifyFunctionExitNodesPass;
+
+inline uint32_t getVectorElementCount(const llvm::VectorType *VecTy) {
+    auto ElementCount = VecTy->getElementCount();
+    return ElementCount.getFixedValue();
+}
+
+//
+// PatternMatch
+//
+
+#define compat_m_InsertElt llvm::PatternMatch::m_InsertElt
+#define compat_m_Shuffle llvm::PatternMatch::m_Shuffle
+#define compat_m_ZeroMask llvm::PatternMatch::m_ZeroMask
+
+} // namespace compat
diff --git a/subprojects/helper-to-tcg/meson.build b/subprojects/helper-to-tcg/meson.build
index 97bce186fe..965014c988 100644
--- a/subprojects/helper-to-tcg/meson.build
+++ b/subprojects/helper-to-tcg/meson.build
@@ -40,6 +40,7 @@ if version_major < 10 or version_major > 21
 endif
 
 sources = [
+    'src/LlvmCompat.cpp',
 ]
 
 clang = bindir / 'clang'
diff --git a/subprojects/helper-to-tcg/src/LlvmCompat.cpp b/subprojects/helper-to-tcg/src/LlvmCompat.cpp
new file mode 100644
index 0000000000..3a1995391c
--- /dev/null
+++ b/subprojects/helper-to-tcg/src/LlvmCompat.cpp
@@ -0,0 +1,89 @@
+//
+//  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 "LlvmCompat.hpp"
+
+#include <llvm/CodeGen/CommandFlags.h>
+#include <llvm/Support/CodeGen.h>
+
+#include <string>
+
+// Static variables required by LLVM
+//
+// Defining RegisterCodeGenFlags with static duration registers extra
+// codegen commandline flags for specifying the target arch.
+static llvm::codegen::RegisterCodeGenFlags CGF;
+static llvm::ExitOnError ExitOnErr;
+
+namespace compat {
+
+using namespace llvm;
+
+size_t getTypeAllocSize(const llvm::DataLayout &DL, llvm::Type *Ty) {
+#if LLVM_VERSION_MAJOR >= 16
+    return DL.getTypeAllocSize(Ty).getFixedValue();
+#else
+    return DL.getTypeAllocSize(Ty).getFixedSize();
+#endif
+}
+
+bool isFunctionQemuHelper(StringRef Name) {
+#if LLVM_VERSION_MAJOR >= 18
+    return Name.starts_with("helper_");
+#else
+    return Name.startswith("helper_");
+#endif
+}
+
+bool isFunctionQemuLoadStore(StringRef Name) {
+#if LLVM_VERSION_MAJOR >= 18
+    return Name.starts_with("cpu_ld") or Name.starts_with("cpu_st");
+#else
+    return Name.startswith("cpu_ld") or Name.startswith("cpu_st");
+#endif
+}
+
+llvm::TargetMachine *getTargetMachine(llvm::Triple &TheTriple) {
+    const TargetOptions Options{};
+    std::string Error;
+    const Target *TheTarget = llvm::TargetRegistry::lookupTarget(
+        llvm::codegen::getMArch(), TheTriple, Error);
+    // Some modules don't specify a triple, and this is okay.
+    if (!TheTarget) {
+        return nullptr;
+    }
+
+#if LLVM_VERSION_MAJOR >= 18
+    auto Level = llvm::CodeGenOptLevel::Aggressive;
+#else
+    auto Level = llvm::CodeGenOpt::Aggressive;
+#endif
+
+#if LLVM_VERSION_MAJOR >= 21
+    return TheTarget->createTargetMachine(
+        llvm::Triple(TheTriple.getTriple()), llvm::codegen::getCPUStr(),
+        llvm::codegen::getFeaturesStr(), Options,
+        llvm::codegen::getExplicitRelocModel(),
+        llvm::codegen::getExplicitCodeModel(), Level);
+#else
+    return TheTarget->createTargetMachine(
+        TheTriple.getTriple(), llvm::codegen::getCPUStr(),
+        llvm::codegen::getFeaturesStr(), Options, {}, {}, Level);
+#endif
+}
+
+} // namespace compat
-- 
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.