[PATCH v2 25/50] helper-to-tcg: PrepareForTcgPass, map TCG globals

Anton Johansson via qemu development <[email protected]>
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
The input LLVM module may define an array of cpu_mapping structs,
describing the mapping between fields in a specified struct (usually
CPUArchState) and TCG globals.

Create a map between offsets into the specified struct and TCG globals
(name, size, number of elements, stride) by iterating over the global
cpu_mapping array.  The name of this array is configurable via
the --tcg-global-mappings flag.

Signed-off-by: Anton Johansson <[email protected]>
---
 .../include/PrepareForTcgPass.hpp             |  6 +-
 .../helper-to-tcg/include/TcgGlobalMap.hpp    | 40 ++++++++++++
 subprojects/helper-to-tcg/src/Pipeline.cpp    |  9 +--
 .../PrepareForTcgPass/PrepareForTcgPass.cpp   | 62 +++++++++++++++++++
 4 files changed, 112 insertions(+), 5 deletions(-)
 create mode 100644 subprojects/helper-to-tcg/include/TcgGlobalMap.hpp

diff --git a/subprojects/helper-to-tcg/include/PrepareForTcgPass.hpp b/subprojects/helper-to-tcg/include/PrepareForTcgPass.hpp
index 90bc9402cb..3e0679b46c 100644
--- a/subprojects/helper-to-tcg/include/PrepareForTcgPass.hpp
+++ b/subprojects/helper-to-tcg/include/PrepareForTcgPass.hpp
@@ -17,11 +17,15 @@
 
 #pragma once
 
+#include "TcgGlobalMap.hpp"
 #include <llvm/IR/PassManager.h>
 
 class PrepareForTcgPass : public llvm::PassInfoMixin<PrepareForTcgPass> {
+    TcgGlobalMap &ResultTcgGlobalMap;
+
 public:
-    PrepareForTcgPass() {}
+    PrepareForTcgPass(TcgGlobalMap &ResultTcgGlobalMap)
+        : ResultTcgGlobalMap(ResultTcgGlobalMap) {}
     llvm::PreservedAnalyses run(llvm::Module &M,
                                 llvm::ModuleAnalysisManager &MAM);
 };
diff --git a/subprojects/helper-to-tcg/include/TcgGlobalMap.hpp b/subprojects/helper-to-tcg/include/TcgGlobalMap.hpp
new file mode 100644
index 0000000000..ad7ac54608
--- /dev/null
+++ b/subprojects/helper-to-tcg/include/TcgGlobalMap.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+//
+//  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 <llvm/ADT/DenseMap.h>
+#include <llvm/ADT/SmallVector.h>
+#include <llvm/ADT/StringRef.h>
+#include <stdint.h>
+
+// `TcgGlobal` describes a field in a struct which has a mapping to a TCG
+// global.  `Size`, `NumElements`, and `Stride` describe the mapped type, all
+// types are assumed to be integer or arrays of integers.  `Code` is the
+// expression to be emitted when accessing the mapped global, usually the
+// variable name.
+struct TcgGlobal {
+    llvm::StringRef Code;
+    uint64_t Size;
+    uint64_t NumElements;
+    uint64_t Stride;
+};
+
+// Array of maps between offsets into a mapped struct to the resulting global
+// type, outer array is indexed by the base struct type to handle multiple
+// struct-to-global mappings.
+using TcgGlobalMap = llvm::SmallVector<llvm::DenseMap<uint32_t, TcgGlobal>, 1>;
diff --git a/subprojects/helper-to-tcg/src/Pipeline.cpp b/subprojects/helper-to-tcg/src/Pipeline.cpp
index 6058718a9c..f24bb67f0c 100644
--- a/subprojects/helper-to-tcg/src/Pipeline.cpp
+++ b/subprojects/helper-to-tcg/src/Pipeline.cpp
@@ -76,9 +76,9 @@ cl::opt<bool> TranslateAllHelpers(
 // Options for PrepareForTcgPass
 cl::opt<std::string> TcgGlobalMappingsName(
     "tcg-global-mappings",
-    cl::desc("<Name of global cpu_mappings[] used for mapping accesses"
-             "into a struct to TCG globals>"),
-    cl::Required, cl::cat(Cat));
+    cl::desc("Name of global cpu_mappings[] used for mapping accesses"
+             "into a struct to TCG globals"),
+    cl::init("mappings"), cl::cat(Cat));
 
 // Define a TargetTransformInfo (TTI) subclass, this allows for overriding
 // common per-llvm-target information expected by other LLVM passes, such
@@ -230,7 +230,8 @@ int main(int argc, char **argv) {
     // easily to TCG.
     //
 
-    MPM.addPass(PrepareForTcgPass());
+    TcgGlobalMap TcgGlobals;
+    MPM.addPass(PrepareForTcgPass(TcgGlobals));
     MPM.addPass(VerifierPass());
     {
         FunctionPassManager FPM;
diff --git a/subprojects/helper-to-tcg/src/PrepareForTcgPass/PrepareForTcgPass.cpp b/subprojects/helper-to-tcg/src/PrepareForTcgPass/PrepareForTcgPass.cpp
index d8530406ad..69add43529 100644
--- a/subprojects/helper-to-tcg/src/PrepareForTcgPass/PrepareForTcgPass.cpp
+++ b/subprojects/helper-to-tcg/src/PrepareForTcgPass/PrepareForTcgPass.cpp
@@ -16,9 +16,12 @@
 //
 
 #include "PrepareForTcgPass.hpp"
+#include "CmdLineOptions.hpp"
 
 #include <llvm/ADT/SCCIterator.h>
 #include <llvm/ADT/SmallPtrSet.h>
+#include <llvm/ADT/StringMap.h>
+#include <llvm/IR/Constants.h>
 #include <llvm/IR/Function.h>
 #include <llvm/IR/InstIterator.h>
 #include <llvm/IR/Instructions.h>
@@ -68,11 +71,70 @@ inline void demotePhis(Function &F) {
     }
 }
 
+static StringMap<size_t> collectTcgGlobals(Module &M, TcgGlobalMap &ResultTcgGlobalMap) {
+    auto *Map = M.getGlobalVariable(TcgGlobalMappingsName);
+    if (!Map) {
+        return {};
+    }
+
+    // In case the `tcg_global_mappings` array is empty,
+    // casting to `ConstantArray` will fail, even though it's a
+    // `[0 x %struct.cpu_tcg_mapping]`.
+    auto *MapElems = dyn_cast<ConstantArray>(Map->getOperand(0));
+    if (!MapElems) {
+        return {};
+    }
+
+    StringMap<size_t> TypeIndexMap;
+
+    for (auto Row : MapElems->operand_values()) {
+        auto *ConstRow = cast<ConstantStruct>(Row);
+
+        // Get code string
+        auto *CodePtr = ConstRow->getOperand(0);
+        StringRef CodeStr =
+            cast<ConstantDataArray>(CodePtr->getOperand(0))->getAsString();
+        CodeStr = CodeStr.rtrim('\0');
+
+        // Get base type name
+        auto *TypeNamePtr = ConstRow->getOperand(2);
+        StringRef TypeNameStr =
+            cast<ConstantDataArray>(TypeNamePtr->getOperand(0))->getAsString();
+        TypeNameStr = TypeNameStr.rtrim('\0');
+
+        // Get offset in cpu env
+        auto *Offset = cast<ConstantInt>(ConstRow->getOperand(4));
+        // Get size of variable in cpu env
+        auto *SizeInBytes = cast<ConstantInt>(ConstRow->getOperand(5));
+        unsigned SizeInBits = 8 * SizeInBytes->getLimitedValue();
+
+        auto *Stride = cast<ConstantInt>(ConstRow->getOperand(6));
+        auto *NumElements = cast<ConstantInt>(ConstRow->getOperand(7));
+
+        if (auto It = TypeIndexMap.find(TypeNameStr);
+            It == TypeIndexMap.end()) {
+            TypeIndexMap[TypeNameStr] = ResultTcgGlobalMap.size();
+            ResultTcgGlobalMap.emplace_back();
+        }
+
+        const size_t Index = TypeIndexMap[TypeNameStr];
+        ResultTcgGlobalMap[Index][Offset->getLimitedValue()] = {
+            CodeStr,
+            SizeInBits,
+            NumElements->getLimitedValue(),
+            Stride->getLimitedValue(),
+        };
+    }
+
+    return TypeIndexMap;
+}
+
 PreservedAnalyses PrepareForTcgPass::run(Module &M,
                                          ModuleAnalysisManager &MAM) {
     removeFunctionsWithLoops(M, MAM);
     for (Function &F : M) {
         demotePhis(F);
     }
+    const StringMap<size_t> TypeIndexMap = collectTcgGlobals(M, ResultTcgGlobalMap);
     return PreservedAnalyses::none();
 }
-- 
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.