[llvm] [Draft] Basic JITLink AArch32 support for clang-repl (PR #77313)

Stefan Gränitz via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 15 04:18:58 PST 2024


https://github.com/weliveindetail updated https://github.com/llvm/llvm-project/pull/77313

>From b50f4001ceba87a531143ad85ffe05cf1ed1d2d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Mon, 8 Jan 2024 15:05:08 +0100
Subject: [PATCH 1/7] [JITLink][AArch32] In warning output add decimal value
 for CPUArch and missing newline

---
 llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
index f346cfb2a93112..d7869377a8baaa 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
@@ -146,7 +146,7 @@ inline ArmConfig getArmConfigForCPUArch(ARMBuildAttrs::CPUArch CPUArch) {
   default:
     DEBUG_WITH_TYPE("jitlink", {
       dbgs() << "  Warning: ARM config not defined for CPU architecture "
-             << getCPUArchName(CPUArch);
+             << getCPUArchName(CPUArch) << " (" << CPUArch << ")\n";
     });
     break;
   }

>From 36e9566e04577182ac6a456aac139d7b2cf54889 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Wed, 10 Jan 2024 17:07:28 +0100
Subject: [PATCH 2/7] [JITLink][AArch32] Streamline file-names of tests (NFC)

All other backends use the full term "relocations". Also, sorting by type (relocations/stubs/etc.) before CPU states (arm/thumb/other) makes it easier to filter in LIT.
---
 .../AArch32/{ELF_static_arm_reloc.s => ELF_relocations_arm.s}     | 0
 .../AArch32/{ELF_static_data_reloc.s => ELF_relocations_data.s}   | 0
 .../AArch32/{ELF_static_thumb_reloc.s => ELF_relocations_thumb.s} | 0
 .../JITLink/AArch32/{ELF_thumb_stubs.s => ELF_stubs_thumb.s}      | 0
 4 files changed, 0 insertions(+), 0 deletions(-)
 rename llvm/test/ExecutionEngine/JITLink/AArch32/{ELF_static_arm_reloc.s => ELF_relocations_arm.s} (100%)
 rename llvm/test/ExecutionEngine/JITLink/AArch32/{ELF_static_data_reloc.s => ELF_relocations_data.s} (100%)
 rename llvm/test/ExecutionEngine/JITLink/AArch32/{ELF_static_thumb_reloc.s => ELF_relocations_thumb.s} (100%)
 rename llvm/test/ExecutionEngine/JITLink/AArch32/{ELF_thumb_stubs.s => ELF_stubs_thumb.s} (100%)

diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_arm.s
similarity index 100%
rename from llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s
rename to llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_arm.s
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_data_reloc.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s
similarity index 100%
rename from llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_data_reloc.s
rename to llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_thumb_reloc.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_thumb.s
similarity index 100%
rename from llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_thumb_reloc.s
rename to llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_thumb.s
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_thumb_stubs.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_thumb.s
similarity index 100%
rename from llvm/test/ExecutionEngine/JITLink/AArch32/ELF_thumb_stubs.s
rename to llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_thumb.s

>From ed26d08ada2d6c9a335597181772e518293ab24d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Mon, 8 Jan 2024 15:06:33 +0100
Subject: [PATCH 3/7] [Orc] Make JITLink default in LLJIT for ELF-based ARM
 targets

---
 llvm/lib/ExecutionEngine/Orc/LLJIT.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
index e259c393d07e03..833dcb9d5bf2e7 100644
--- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
@@ -734,6 +734,12 @@ Error LLJITBuilderState::prepareForConstruction() {
     case Triple::aarch64:
       UseJITLink = !TT.isOSBinFormatCOFF();
       break;
+    case Triple::arm:
+    case Triple::armeb:
+    case Triple::thumb:
+    case Triple::thumbeb:
+      UseJITLink = TT.isOSBinFormatELF();
+      break;
     case Triple::x86_64:
       UseJITLink = !TT.isOSBinFormatCOFF();
       break;

>From 9913d3e67638ac4dcd0917a4dc8746f56a5302b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Sat, 13 Jan 2024 23:29:33 +0100
Subject: [PATCH 4/7] [JITLink][AArch32] Fix typo in thumb stubs test (NFC)

---
 llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_thumb.s | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_thumb.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_thumb.s
index bd95c375279246..598d9d06100887 100644
--- a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_thumb.s
+++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_thumb.s
@@ -26,7 +26,7 @@ test_external_call:
 	.size test_external_call, .-test_external_call
 
 	.globl  test_external_jump
-	.type	test_external_call,%function
+	.type	test_external_jump,%function
 	.p2align	1
 	.code	16
 	.thumb_func

>From 5083c866901bf405260f95802fbedbec4009f7d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Sat, 13 Jan 2024 23:35:54 +0100
Subject: [PATCH 5/7] [Orc] Allow optional index argument in jitlink-check
 stub_addr expressions

---
 .../llvm/ExecutionEngine/RuntimeDyldChecker.h |  2 +-
 .../RuntimeDyld/RuntimeDyldChecker.cpp        | 21 +++++++++++++----
 .../RuntimeDyld/RuntimeDyldCheckerImpl.h      |  3 ++-
 llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp |  6 ++---
 llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp  | 12 +++++-----
 .../tools/llvm-jitlink/llvm-jitlink-macho.cpp |  6 ++---
 llvm/tools/llvm-jitlink/llvm-jitlink.cpp      | 23 ++++++++++++++-----
 llvm/tools/llvm-jitlink/llvm-jitlink.h        |  6 ++---
 8 files changed, 51 insertions(+), 28 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h b/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h
index 80e4bbf494339c..55cc16834976d0 100644
--- a/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h
+++ b/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h
@@ -154,7 +154,7 @@ class RuntimeDyldChecker {
   using GetSectionInfoFunction = std::function<Expected<MemoryRegionInfo>(
       StringRef FileName, StringRef SectionName)>;
   using GetStubInfoFunction = std::function<Expected<MemoryRegionInfo>(
-      StringRef StubContainer, StringRef TargetName)>;
+      StringRef StubContainer, StringRef TargetName, int64_t StubIndex)>;
   using GetGOTInfoFunction = std::function<Expected<MemoryRegionInfo>(
       StringRef GOTContainer, StringRef TargetName)>;
 
diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp
index 7fadbdd6a1fff2..801496b8b91035 100644
--- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp
+++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp
@@ -400,6 +400,15 @@ class RuntimeDyldCheckerExprEval {
     StringRef Symbol;
     std::tie(Symbol, RemainingExpr) = parseSymbol(RemainingExpr);
 
+    // Parse optional stub index parameter
+    int64_t StubIndex = 0;
+    if (RemainingExpr.starts_with(",")) {
+      RemainingExpr = RemainingExpr.substr(1).ltrim();
+      EvalResult Number;
+      std::tie(Number, RemainingExpr) = evalNumberExpr(RemainingExpr);
+      StubIndex = Number.getValue();
+    }
+
     if (!RemainingExpr.starts_with(")"))
       return std::make_pair(
           unexpectedToken(RemainingExpr, Expr, "expected ')'"), "");
@@ -408,7 +417,7 @@ class RuntimeDyldCheckerExprEval {
     uint64_t StubAddr;
     std::string ErrorMsg;
     std::tie(StubAddr, ErrorMsg) = Checker.getStubOrGOTAddrFor(
-        StubContainerName, Symbol, PCtx.IsInsideLoad, IsStubAddr);
+        StubContainerName, Symbol, StubIndex, PCtx.IsInsideLoad, IsStubAddr);
 
     if (ErrorMsg != "")
       return std::make_pair(EvalResult(ErrorMsg), "");
@@ -985,11 +994,13 @@ std::pair<uint64_t, std::string> RuntimeDyldCheckerImpl::getSectionAddr(
 }
 
 std::pair<uint64_t, std::string> RuntimeDyldCheckerImpl::getStubOrGOTAddrFor(
-    StringRef StubContainerName, StringRef SymbolName, bool IsInsideLoad,
-    bool IsStubAddr) const {
+    StringRef StubContainerName, StringRef SymbolName, int64_t StubIndex,
+    bool IsInsideLoad, bool IsStubAddr) const {
 
-  auto StubInfo = IsStubAddr ? GetStubInfo(StubContainerName, SymbolName)
-                             : GetGOTInfo(StubContainerName, SymbolName);
+  assert((StubIndex == 0 || IsStubAddr) && "Indexing only supported for stubs");
+  auto StubInfo = IsStubAddr
+                      ? GetStubInfo(StubContainerName, SymbolName, StubIndex)
+                      : GetGOTInfo(StubContainerName, SymbolName);
 
   if (!StubInfo) {
     std::string ErrMsg;
diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCheckerImpl.h b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCheckerImpl.h
index 9f44a9389f4734..0f43e877318895 100644
--- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCheckerImpl.h
+++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCheckerImpl.h
@@ -64,7 +64,8 @@ class RuntimeDyldCheckerImpl {
 
   std::pair<uint64_t, std::string>
   getStubOrGOTAddrFor(StringRef StubContainerName, StringRef Symbol,
-                      bool IsInsideLoad, bool IsStubAddr) const;
+                      int64_t StubIndex, bool IsInsideLoad,
+                      bool IsStubAddr) const;
 
   std::optional<uint64_t> getSectionLoadAddress(void *LocalAddr) const;
 
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp
index 283e655205d780..5857ae88d3bb93 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp
@@ -130,9 +130,9 @@ Error registerCOFFGraphInfo(Session &S, LinkGraph &G) {
                                          inconvertibleErrorCode());
 
         if (auto TS = getCOFFStubTarget(G, Sym->getBlock()))
-          FileInfo.StubInfos[TS->getName()] = {Sym->getSymbolContent(),
-                                               Sym->getAddress().getValue(),
-                                               Sym->getTargetFlags()};
+          FileInfo.StubInfos[TS->getName()].insert(
+              0, {Sym->getSymbolContent(), Sym->getAddress().getValue(),
+                  Sym->getTargetFlags()});
         else
           return TS.takeError();
         SectionContainsContent = true;
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp
index a02468758b33f6..395d16308fe2fb 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp
@@ -150,9 +150,9 @@ Error registerELFGraphInfo(Session &S, LinkGraph &G) {
                                          inconvertibleErrorCode());
 
         if (auto TS = getELFStubTarget(G, Sym->getBlock()))
-          FileInfo.StubInfos[TS->getName()] = {Sym->getSymbolContent(),
-                                               Sym->getAddress().getValue(),
-                                               Sym->getTargetFlags()};
+          FileInfo.StubInfos[TS->getName()].insert(
+              0, {Sym->getSymbolContent(), Sym->getAddress().getValue(),
+                  Sym->getTargetFlags()});
         else
           return TS.takeError();
         SectionContainsContent = true;
@@ -162,9 +162,9 @@ Error registerELFGraphInfo(Session &S, LinkGraph &G) {
                                          inconvertibleErrorCode());
 
         if (auto Name = getELFAArch32StubTargetName(G, Sym->getBlock()))
-          FileInfo.StubInfos[*Name] = {Sym->getSymbolContent(),
-                                       Sym->getAddress().getValue(),
-                                       Sym->getTargetFlags()};
+          FileInfo.StubInfos[*Name].push_back({Sym->getSymbolContent(),
+                                               Sym->getAddress().getValue(),
+                                               Sym->getTargetFlags()});
         else
           return Name.takeError();
         SectionContainsContent = true;
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink-macho.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink-macho.cpp
index 7dcadd94c2365a..aeb9e84a2946cf 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink-macho.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink-macho.cpp
@@ -129,9 +129,9 @@ Error registerMachOGraphInfo(Session &S, LinkGraph &G) {
                                          inconvertibleErrorCode());
 
         if (auto TS = getMachOStubTarget(G, Sym->getBlock()))
-          FileInfo.StubInfos[TS->getName()] = {Sym->getSymbolContent(),
-                                               Sym->getAddress().getValue(),
-                                               Sym->getTargetFlags()};
+          FileInfo.StubInfos[TS->getName()].insert(
+              0, {Sym->getSymbolContent(), Sym->getAddress().getValue(),
+                  Sym->getTargetFlags()});
         else
           return TS.takeError();
         SectionContainsContent = true;
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
index aa032c97485fbf..10327c3c768fa7 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
@@ -331,8 +331,12 @@ operator<<(raw_ostream &OS, const Session::FileInfo &FI) {
     OS << "  Section \"" << SIKV.first() << "\": " << SIKV.second << "\n";
   for (auto &GOTKV : FI.GOTEntryInfos)
     OS << "  GOT \"" << GOTKV.first() << "\": " << GOTKV.second << "\n";
-  for (auto &StubKV : FI.StubInfos)
-    OS << "  Stub \"" << StubKV.first() << "\": " << StubKV.second << "\n";
+  for (auto &StubKVs : FI.StubInfos) {
+    OS << "  Stubs \"" << StubKVs.first() << "\":";
+    for (auto MemRegion : StubKVs.second)
+      OS << " " << MemRegion;
+    OS << "\n";
+  }
   return OS;
 }
 
@@ -1206,7 +1210,8 @@ Session::findSectionInfo(StringRef FileName, StringRef SectionName) {
 }
 
 Expected<Session::MemoryRegionInfo &>
-Session::findStubInfo(StringRef FileName, StringRef TargetName) {
+Session::findStubInfo(StringRef FileName, StringRef TargetName,
+                      uint64_t StubIndex) {
   auto FI = findFileInfo(FileName);
   if (!FI)
     return FI.takeError();
@@ -1216,7 +1221,12 @@ Session::findStubInfo(StringRef FileName, StringRef TargetName) {
                                        "\" registered for file \"" + FileName +
                                        "\"",
                                    inconvertibleErrorCode());
-  return StubInfoItr->second;
+  if (StubIndex >= StubInfoItr->second.size())
+    return make_error<StringError>(
+        "no stub for \"" + TargetName + " with index " + Twine(StubIndex) +
+            "\" registered for file \"" + FileName + "\"",
+        inconvertibleErrorCode());
+  return StubInfoItr->second[StubIndex];
 }
 
 Expected<Session::MemoryRegionInfo &>
@@ -1985,8 +1995,9 @@ static Error runChecks(Session &S, Triple TT, SubtargetFeatures Features) {
     return S.findSectionInfo(FileName, SectionName);
   };
 
-  auto GetStubInfo = [&S](StringRef FileName, StringRef SectionName) {
-    return S.findStubInfo(FileName, SectionName);
+  auto GetStubInfo = [&S](StringRef FileName, StringRef SectionName,
+                          int64_t StubIndex) {
+    return S.findStubInfo(FileName, SectionName, StubIndex);
   };
 
   auto GetGOTInfo = [&S](StringRef FileName, StringRef SectionName) {
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.h b/llvm/tools/llvm-jitlink/llvm-jitlink.h
index 3ff406b7b82dfd..c3ed8981e1daf1 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.h
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.h
@@ -49,7 +49,7 @@ struct Session {
 
   struct FileInfo {
     StringMap<MemoryRegionInfo> SectionInfos;
-    StringMap<MemoryRegionInfo> StubInfos;
+    StringMap<SmallVector<MemoryRegionInfo, 1>> StubInfos;
     StringMap<MemoryRegionInfo> GOTEntryInfos;
   };
 
@@ -63,8 +63,8 @@ struct Session {
   Expected<FileInfo &> findFileInfo(StringRef FileName);
   Expected<MemoryRegionInfo &> findSectionInfo(StringRef FileName,
                                                StringRef SectionName);
-  Expected<MemoryRegionInfo &> findStubInfo(StringRef FileName,
-                                            StringRef TargetName);
+  Expected<MemoryRegionInfo &>
+  findStubInfo(StringRef FileName, StringRef TargetName, uint64_t StubIndex);
   Expected<MemoryRegionInfo &> findGOTEntryInfo(StringRef FileName,
                                                 StringRef TargetName);
 

>From 9730b7b81eaabd456d9ef10f1f886c2450f3db5b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Sat, 13 Jan 2024 23:45:15 +0100
Subject: [PATCH 6/7] [JITLink][AArch32] Rename stubs flavor Thumbv7 to v7
 (NFC)

---
 llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h | 12 ++++++------
 llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp    |  8 ++++----
 llvm/lib/ExecutionEngine/JITLink/aarch32.cpp        |  2 +-
 3 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
index d7869377a8baaa..53c9ef2fdf395e 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
@@ -123,15 +123,15 @@ const char *getEdgeKindName(Edge::Kind K);
 ///
 /// Stubs are often called "veneers" in the official docs and online.
 ///
-enum StubsFlavor {
+enum class StubsFlavor {
   Unsupported = 0,
-  Thumbv7,
+  v7,
 };
 
 /// JITLink sub-arch configuration for Arm CPU models
 struct ArmConfig {
   bool J1J2BranchEncoding = false;
-  StubsFlavor Stubs = Unsupported;
+  StubsFlavor Stubs = StubsFlavor::Unsupported;
 };
 
 /// Obtain the sub-arch configuration for a given Arm CPU model.
@@ -141,7 +141,7 @@ inline ArmConfig getArmConfigForCPUArch(ARMBuildAttrs::CPUArch CPUArch) {
   case ARMBuildAttrs::v7:
   case ARMBuildAttrs::v8_A:
     ArmCfg.J1J2BranchEncoding = true;
-    ArmCfg.Stubs = Thumbv7;
+    ArmCfg.Stubs = StubsFlavor::v7;
     break;
   default:
     DEBUG_WITH_TYPE("jitlink", {
@@ -380,9 +380,9 @@ class StubsManager : public TableManager<StubsManager<Flavor>> {
 
 /// Create a branch range extension stub with Thumb encoding for v7 CPUs.
 template <>
-Symbol &StubsManager<Thumbv7>::createEntry(LinkGraph &G, Symbol &Target);
+Symbol &StubsManager<StubsFlavor::v7>::createEntry(LinkGraph &G, Symbol &Target);
 
-template <> inline StringRef StubsManager<Thumbv7>::getSectionName() {
+template <> inline StringRef StubsManager<StubsFlavor::v7>::getSectionName() {
   return "__llvm_jitlink_aarch32_STUBS_Thumbv7";
 }
 
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
index 132989fcbce021..3c596a414363e4 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
@@ -257,7 +257,7 @@ createLinkGraphFromELFObject_aarch32(MemoryBufferRef ObjectBuffer) {
   case v7:
   case v8_A:
     ArmCfg = aarch32::getArmConfigForCPUArch(Arch);
-    assert(ArmCfg.Stubs != aarch32::Unsupported &&
+    assert(ArmCfg.Stubs != aarch32::StubsFlavor::Unsupported &&
            "Provide a config for each supported CPU");
     break;
   default:
@@ -309,11 +309,11 @@ void link_ELF_aarch32(std::unique_ptr<LinkGraph> G,
       PassCfg.PrePrunePasses.push_back(markAllSymbolsLive);
 
     switch (ArmCfg.Stubs) {
-    case aarch32::Thumbv7:
+    case aarch32::StubsFlavor::v7:
       PassCfg.PostPrunePasses.push_back(
-          buildTables_ELF_aarch32<aarch32::Thumbv7>);
+          buildTables_ELF_aarch32<aarch32::StubsFlavor::v7>);
       break;
-    case aarch32::Unsupported:
+    case aarch32::StubsFlavor::Unsupported:
       llvm_unreachable("Check before building graph");
     }
   }
diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
index 671ee1a8125252..4e75bb4882a28a 100644
--- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
@@ -685,7 +685,7 @@ const uint8_t Thumbv7ABS[] = {
 };
 
 template <>
-Symbol &StubsManager<Thumbv7>::createEntry(LinkGraph &G, Symbol &Target) {
+Symbol &StubsManager<StubsFlavor::v7>::createEntry(LinkGraph &G, Symbol &Target) {
   constexpr uint64_t Alignment = 4;
   Block &B = addStub(G, Thumbv7ABS, Alignment);
   LLVM_DEBUG({

>From 13ce9e6c1e36bb7675ab74226bdf4b03a6f76ac4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Sat, 13 Jan 2024 23:58:36 +0100
Subject: [PATCH 7/7] [JITLink][AArch32] Multi-stub support and selection
 armv7/thumbv7

---
 .../llvm/ExecutionEngine/JITLink/aarch32.h    |  66 ++------
 .../ExecutionEngine/JITLink/ELF_aarch32.cpp   |   8 +-
 llvm/lib/ExecutionEngine/JITLink/aarch32.cpp  | 144 ++++++++++++++++--
 .../JITLink/AArch32/ELF_stubs_arm.s           |  39 +++++
 .../JITLink/AArch32/ELF_stubs_multi.s         |  56 +++++++
 5 files changed, 242 insertions(+), 71 deletions(-)
 create mode 100644 llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s
 create mode 100644 llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_multi.s

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
index 53c9ef2fdf395e..44f11f6162be24 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
@@ -318,74 +318,32 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
   llvm_unreachable("Relocation must be of class Data, Arm or Thumb");
 }
 
-/// Stubs builder for a specific StubsFlavor
-///
-/// Right now we only have one default stub kind, but we want to extend this
-/// and allow creation of specific kinds in the future (e.g. branch range
-/// extension or interworking).
-///
+/// Stubs builder for v7 emits non-position-independent Arm and Thumb stubs.
 /// Let's keep it simple for the moment and not wire this through a GOT.
 ///
-template <StubsFlavor Flavor>
-class StubsManager : public TableManager<StubsManager<Flavor>> {
+class StubsManager_v7 {
 public:
-  StubsManager() = default;
 
-  /// Name of the object file section that will contain all our stubs.
-  static StringRef getSectionName();
+  StubsManager_v7() = default;
 
-  /// Implements link-graph traversal via visitExistingEdges().
-  bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
-    if (E.getTarget().isDefined())
-      return false;
-
-    switch (E.getKind()) {
-    case Thumb_Call:
-    case Thumb_Jump24: {
-      DEBUG_WITH_TYPE("jitlink", {
-        dbgs() << "  Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
-               << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
-               << formatv("{0:x}", E.getOffset()) << ")\n";
-      });
-      E.setTarget(this->getEntryForTarget(G, E.getTarget()));
-      return true;
-    }
-    }
-    return false;
+  /// Name of the object file section that will contain all our stubs.
+  static StringRef getSectionName() {
+    return "__llvm_jitlink_aarch32_STUBS_v7";
   }
 
-  /// Create a branch range extension stub for the class's flavor.
-  Symbol &createEntry(LinkGraph &G, Symbol &Target);
+  /// Implements link-graph traversal via visitExistingEdges().
+  bool visitEdge(LinkGraph &G, Block *B, Edge &E);
 
 private:
-  /// Create a new node in the link-graph for the given stub template.
-  template <size_t Size>
-  Block &addStub(LinkGraph &G, const uint8_t (&Code)[Size],
-                 uint64_t Alignment) {
-    ArrayRef<char> Template(reinterpret_cast<const char *>(Code), Size);
-    return G.createContentBlock(getStubsSection(G), Template,
-                                orc::ExecutorAddr(), Alignment, 0);
-  }
+  using StubMapEntry = SmallVector<std::tuple<bool, Symbol *>>;
+  enum ThumbStatePreference { Undefined = -1, Avoid = 0, PreferNot, Prefer, Force };
 
-  /// Get or create the object file section that will contain all our stubs.
-  Section &getStubsSection(LinkGraph &G) {
-    if (!StubsSection)
-      StubsSection = &G.createSection(getSectionName(),
-                                      orc::MemProt::Read | orc::MemProt::Exec);
-    return *StubsSection;
-  }
+  Symbol *selectStub(const StubMapEntry &Candidates, ThumbStatePreference ThumbState);
 
+  DenseMap<StringRef, StubMapEntry> StubMap;
   Section *StubsSection = nullptr;
 };
 
-/// Create a branch range extension stub with Thumb encoding for v7 CPUs.
-template <>
-Symbol &StubsManager<StubsFlavor::v7>::createEntry(LinkGraph &G, Symbol &Target);
-
-template <> inline StringRef StubsManager<StubsFlavor::v7>::getSectionName() {
-  return "__llvm_jitlink_aarch32_STUBS_Thumbv7";
-}
-
 } // namespace aarch32
 } // namespace jitlink
 } // namespace llvm
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
index 3c596a414363e4..6f77ac89b9c2cd 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
@@ -216,12 +216,12 @@ class ELFLinkGraphBuilder_aarch32
         ArmCfg(std::move(ArmCfg)) {}
 };
 
-template <aarch32::StubsFlavor Flavor>
+template <typename StubsManagerType>
 Error buildTables_ELF_aarch32(LinkGraph &G) {
   LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n");
 
-  aarch32::StubsManager<Flavor> PLT;
-  visitExistingEdges(G, PLT);
+  StubsManagerType Stubs;
+  visitExistingEdges(G, Stubs);
   return Error::success();
 }
 
@@ -311,7 +311,7 @@ void link_ELF_aarch32(std::unique_ptr<LinkGraph> G,
     switch (ArmCfg.Stubs) {
     case aarch32::StubsFlavor::v7:
       PassCfg.PostPrunePasses.push_back(
-          buildTables_ELF_aarch32<aarch32::StubsFlavor::v7>);
+          buildTables_ELF_aarch32<aarch32::StubsManager_v7>);
       break;
     case aarch32::StubsFlavor::Unsupported:
       llvm_unreachable("Check before building graph");
diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
index 4e75bb4882a28a..7932e24b6df245 100644
--- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
@@ -15,6 +15,7 @@
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/ExecutionEngine/JITLink/JITLink.h"
+#include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
 #include "llvm/Object/ELFObjectFile.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/ManagedStatic.h"
@@ -678,28 +679,145 @@ Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E,
   }
 }
 
+const uint8_t Armv7ABS[] = {
+    0x00, 0xc0, 0x00, 0xe3, // movw r12, #0x0000     ; lower 16-bit
+    0x00, 0xc0, 0x40, 0xe3, // movt r12, #0x0000     ; upper 16-bit
+    0x1c, 0xff, 0x2f, 0xe1  // bx   r12
+};
+
 const uint8_t Thumbv7ABS[] = {
     0x40, 0xf2, 0x00, 0x0c, // movw r12, #0x0000    ; lower 16-bit
     0xc0, 0xf2, 0x00, 0x0c, // movt r12, #0x0000    ; upper 16-bit
     0x60, 0x47              // bx   r12
 };
 
-template <>
-Symbol &StubsManager<StubsFlavor::v7>::createEntry(LinkGraph &G, Symbol &Target) {
+/// Create a new node in the link-graph for the given stub template.
+template <size_t Size>
+static Block &allocStub(LinkGraph &G, Section &S, const uint8_t (&Code)[Size]) {
   constexpr uint64_t Alignment = 4;
-  Block &B = addStub(G, Thumbv7ABS, Alignment);
-  LLVM_DEBUG({
-    const char *StubPtr = B.getContent().data();
-    HalfWords Reg12 = encodeRegMovtT1MovwT3(12);
-    assert(checkRegister<Thumb_MovwAbsNC>(StubPtr, Reg12) &&
-           checkRegister<Thumb_MovtAbs>(StubPtr + 4, Reg12) &&
-           "Linker generated stubs may only corrupt register r12 (IP)");
-  });
+  ArrayRef<char> Template(reinterpret_cast<const char *>(Code), Size);
+  return G.createContentBlock(S, Template, orc::ExecutorAddr(), Alignment, 0);
+}
+
+static Block &createStubThumbv7(LinkGraph &G, Section &S, Symbol &Target) {
+  Block &B = allocStub(G, S, Thumbv7ABS);
   B.addEdge(Thumb_MovwAbsNC, 0, Target, 0);
   B.addEdge(Thumb_MovtAbs, 4, Target, 0);
-  Symbol &Stub = G.addAnonymousSymbol(B, 0, B.getSize(), true, false);
-  Stub.setTargetFlags(ThumbSymbol);
-  return Stub;
+
+  [[maybe_unused]] const char *StubPtr = B.getContent().data();
+  [[maybe_unused]] HalfWords Reg12 = encodeRegMovtT1MovwT3(12);
+  assert(checkRegister<Thumb_MovwAbsNC>(StubPtr, Reg12) &&
+         checkRegister<Thumb_MovtAbs>(StubPtr + 4, Reg12) &&
+         "Linker generated stubs may only corrupt register r12 (IP)");
+  return B;
+}
+
+static Block &createStubArmv7(LinkGraph &G, Section &S, Symbol &Target) {
+  Block &B = allocStub(G, S, Armv7ABS);
+  B.addEdge(Arm_MovwAbsNC, 0, Target, 0);
+  B.addEdge(Arm_MovtAbs, 4, Target, 0);
+
+  [[maybe_unused]] const char *StubPtr = B.getContent().data();
+  [[maybe_unused]] uint32_t Reg12 = encodeRegMovtA1MovwA2(12);
+  assert(checkRegister<Arm_MovwAbsNC>(StubPtr, Reg12) &&
+         checkRegister<Arm_MovtAbs>(StubPtr + 4, Reg12) &&
+         "Linker generated stubs may only corrupt register r12 (IP)");
+  return B;
+}
+
+Symbol *StubsManager_v7::selectStub(const StubMapEntry &Candidates,
+                                    ThumbStatePreference ThumbState) {
+  Symbol *StubSymbol = nullptr;
+  for (auto [StubIsThumb, Sym] : Candidates) {
+    if (StubIsThumb) {
+      if (ThumbState == Avoid)
+        continue; // No match
+      StubSymbol = Sym;
+      break; // Best match
+    }
+    if (ThumbState == Avoid) {
+      StubSymbol = Sym;
+      break; // Best match
+    }
+    if (ThumbState == Prefer) {
+      StubSymbol = Sym;
+      continue; // Acceptable match
+    }
+  }
+  return StubSymbol;
+}
+
+bool StubsManager_v7::visitEdge(LinkGraph &G, Block *B, Edge &E) {
+  ThumbStatePreference ThumbState = Undefined;
+  switch (E.getKind()) {
+  case Arm_Call:
+  case Arm_Jump24:
+    ThumbState = Avoid;
+    break;
+  case Thumb_Call:
+    ThumbState = Prefer;
+    break;
+  case Thumb_Jump24:
+    ThumbState = Force;
+    break;
+  default:
+    return false;
+  }
+
+  assert(ThumbState != Undefined && "Define Thumb state preference");
+
+  // Stubs are usually created only for external targets.
+  Symbol &Target = E.getTarget();
+  if (Target.isDefined()) {
+    bool TargetIsThumb = Target.getTargetFlags() & ThumbSymbol;
+    bool InterworkingStub = ThumbState == Force && !TargetIsThumb;
+    if (!InterworkingStub)
+      return false;
+  }
+
+  LLVM_DEBUG(dbgs() << "  Preparing stub with thumb-state '"
+                    << (ThumbState == Avoid ? "Avoid" : "")
+                    << (ThumbState == Prefer ? "Prefer" : "")
+                    << (ThumbState == Force ? "Force" : "") << "' for "
+                    << G.getEdgeKindName(E.getKind()) << " edge at "
+                    << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
+                    << formatv("{0:x}", E.getOffset()) << ")\n");
+
+  assert(Target.hasName() && "Edge cannot point to anonymous target");
+  auto TargetEntry = StubMap.try_emplace(Target.getName()).first;
+
+  Symbol *StubSymbol = selectStub(TargetEntry->second, ThumbState);
+  if (!StubSymbol) {
+    if (!StubsSection)
+      StubsSection = &G.createSection(getSectionName(),
+                                      orc::MemProt::Read | orc::MemProt::Exec);
+
+    bool MakeThumb = ThumbState >= Prefer;
+    Block &B = MakeThumb ? createStubThumbv7(G, *StubsSection, Target)
+                         : createStubArmv7(G, *StubsSection, Target);
+
+    StubSymbol = &G.addAnonymousSymbol(B, 0, B.getSize(), true, false);
+    if (MakeThumb)
+      StubSymbol->setTargetFlags(ThumbSymbol);
+
+    LLVM_DEBUG({
+      dbgs() << "    Created " << (MakeThumb ? "Thumb" : "Arm") << " entry for "
+             << Target.getName() << " in " << StubsSection->getName() << ": "
+             << *StubSymbol << "\n";
+    });
+
+    TargetEntry->second.emplace_back(MakeThumb, StubSymbol);
+  }
+
+  LLVM_DEBUG({
+    bool StubIsThumb = StubSymbol->getTargetFlags() & ThumbSymbol;
+    dbgs() << "    Using " << (StubIsThumb ? "Thumb" : "Arm") << " entry "
+           << *StubSymbol << " in "
+           << StubSymbol->getBlock().getSection().getName() << "\n";
+  });
+
+  E.setTarget(*StubSymbol);
+  return true;
 }
 
 const char *getEdgeKindName(Edge::Kind K) {
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s
new file mode 100644
index 00000000000000..afda0ec9dabf2f
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s
@@ -0,0 +1,39 @@
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: llvm-mc -triple=armv7-linux-gnueabi -arm-add-build-attributes \
+# RUN:         -filetype=obj -filetype=obj -o %t/elf_stubs.o %s
+# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 \
+# RUN:              -slab-allocate 10Kb -slab-page-size 4096 \
+# RUN:              -abs ext_arm=0x76bbe880 \
+# RUN:              -check %s %t/elf_stubs.o
+
+	.text
+	.syntax unified
+
+# Check that calls/jumps to external functions trigger the generation of
+# branch-range extension stubs. These stubs don't follow the default PLT model
+# where the branch-target address is loaded from a GOT entry. Instead, they
+# hard-code it in the immediate field.
+#
+# jitlink-check: decode_operand(test_external_call, 0) = stub_addr(elf_stubs.o, ext_arm) - (test_external_call + 8)
+# jitlink-check: decode_operand(test_external_jump, 0) = stub_addr(elf_stubs.o, ext_arm) - (test_external_jump + 8)
+	.globl  test_external_call
+	.type	test_external_call,%function
+	.p2align	2
+test_external_call:
+	bl	ext_arm
+	.size test_external_call, .-test_external_call
+
+	.globl  test_external_jump
+	.type	test_external_jump,%function
+	.p2align	2
+test_external_jump:
+	b	ext_arm
+	.size test_external_jump, .-test_external_jump
+
+# Empty main function for jitlink to be happy
+	.globl	main
+	.type	main,%function
+	.p2align	2
+main:
+	bx	lr
+	.size	main,	.-main
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_multi.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_multi.s
new file mode 100644
index 00000000000000..2c1a6be85b1a91
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_multi.s
@@ -0,0 +1,56 @@
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: llvm-mc -triple=thumbv7-linux-gnueabi -arm-add-build-attributes \
+# RUN:         -filetype=obj -filetype=obj -o %t/elf_stubs.o %s
+# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 \
+# RUN:              -slab-allocate 10Kb -slab-page-size 4096 \
+# RUN:              -abs external_func=0x76bbe880 \
+# RUN:              -check %s %t/elf_stubs.o
+
+	.text
+	.syntax unified
+
+# Check support for multiple stubs for a single symbol
+
+# jitlink-check: decode_operand(test_avoid_thumb, 0)  = stub_addr(elf_stubs.o, external_func, 0) - (test_avoid_thumb + 8)
+# jitlink-check: decode_operand(test_prefer_thumb, 2) = stub_addr(elf_stubs.o, external_func, 0) - next_pc(test_prefer_thumb)
+# jitlink-check: decode_operand(test_force_thumb, 0)  = stub_addr(elf_stubs.o, external_func, 1) - next_pc(test_force_thumb)
+
+# Arm call will create an Arm stub first
+	.globl  test_avoid_thumb
+	.type	test_avoid_thumb,%function
+	.p2align	2
+	.code	32
+test_avoid_thumb:
+	bl	external_func
+	.size test_avoid_thumb, .-test_avoid_thumb
+
+# Thumb call would create a Thumb stub if there was non at all, but it can be
+# rewritten to BLX and reuse the existing Arm stub.
+# TODO: Should we reconsider during relaxation?
+	.globl  test_prefer_thumb
+	.type	test_prefer_thumb,%function
+	.p2align	1
+	.code	16
+	.thumb_func
+test_prefer_thumb:
+	bl	external_func
+	.size test_prefer_thumb, .-test_prefer_thumb
+
+# Thumb jump requires a Thumb stub, so it creates one in addition to the
+# existing Arm stub.
+	.globl  test_force_thumb
+	.type	test_force_thumb,%function
+	.p2align	1
+	.code	16
+	.thumb_func
+test_force_thumb:
+	b	external_func
+	.size test_force_thumb, .-test_force_thumb
+
+# Empty main function for jitlink to be happy
+	.globl	main
+	.type	main,%function
+	.p2align	2
+main:
+	bx	lr
+	.size	main,	.-main



More information about the llvm-commits mailing list