[lld] [RISCV][LLD] Zcmt RISC-V extension in lld (PR #163142)

Robin Kastberg via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 15 00:15:45 PDT 2025


https://github.com/RobinKastberg updated https://github.com/llvm/llvm-project/pull/163142

>From e0c090e41790732cf8ca4936d5f1cae54d0fce25 Mon Sep 17 00:00:00 2001
From: Robin Kastberg <Robin.Kastberg at iar.com>
Date: Tue, 7 Oct 2025 20:25:01 +0200
Subject: [PATCH 1/8] squash zcmt

---
 lld/ELF/Arch/RISCV.cpp              |  47 ++++++++++++
 lld/ELF/Config.h                    |   3 +
 lld/ELF/Options.td                  |   3 +
 lld/ELF/SyntheticSections.h         |  45 ++++++++++++
 lld/ELF/Target.h                    |   3 +
 lld/test/ELF/riscv-no-tbljal-call.s |  33 +++++++++
 lld/test/ELF/riscv-tbljal-call.s    | 109 ++++++++++++++++++++++++++++
 lld/test/ELF/riscv-tbljal-syms.s    |  42 +++++++++++
 8 files changed, 285 insertions(+)
 create mode 100644 lld/test/ELF/riscv-no-tbljal-call.s
 create mode 100644 lld/test/ELF/riscv-tbljal-call.s
 create mode 100644 lld/test/ELF/riscv-tbljal-syms.s

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index dc2ab97e9d9be..49aadfba00584 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -38,6 +38,8 @@ class RISCV final : public TargetInfo {
   void writePltHeader(uint8_t *buf) const override;
   void writePlt(uint8_t *buf, const Symbol &sym,
                 uint64_t pltEntryAddr) const override;
+  void writeTableJumpHeader(uint8_t *buf) const override;
+  void writeTableJumpEntry(uint8_t *buf, const uint64_t symbol) const override;
   RelType getDynRel(RelType type) const override;
   RelExpr getRelExpr(RelType type, const Symbol &s,
                      const uint8_t *loc) const override;
@@ -70,8 +72,10 @@ class RISCV final : public TargetInfo {
 #define INTERNAL_R_RISCV_GPREL_S 257
 #define INTERNAL_R_RISCV_X0REL_I 258
 #define INTERNAL_R_RISCV_X0REL_S 259
+#define INTERNAL_R_RISCV_TBJAL   260
 
 const uint64_t dtpOffset = 0x800;
+const uint32_t jvtAlign  = 64;
 
 namespace {
 enum Op {
@@ -269,6 +273,20 @@ void RISCV::writePlt(uint8_t *buf, const Symbol &sym,
   write32le(buf + 12, itype(ADDI, 0, 0, 0));
 }
 
+void RISCV::writeTableJumpHeader(uint8_t *buf) const {
+  if (ctx.arg.is64)
+    write64le(buf, ctx.mainPart->dynamic->getVA());
+  else
+    write32le(buf, ctx.mainPart->dynamic->getVA());
+}
+
+void RISCV::writeTableJumpEntry(uint8_t *buf, const uint64_t address) const {
+  if (ctx.arg.is64)
+    write64le(buf, address);
+  else
+    write32le(buf, address);
+}
+
 RelType RISCV::getDynRel(RelType type) const {
   return type == ctx.target->symbolicRel ? type
                                          : static_cast<RelType>(R_RISCV_NONE);
@@ -490,6 +508,9 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     return;
   }
 
+  case INTERNAL_R_RISCV_TBJAL:
+    return;
+
   case R_RISCV_ADD8:
     *loc += val;
     return;
@@ -739,6 +760,30 @@ void elf::initSymbolAnchors(Ctx &ctx) {
   }
 }
 
+static bool relaxTableJump(Ctx &ctx, const InputSection &sec, size_t i, uint64_t loc,
+                           Relocation &r, uint32_t &remove) {
+  if (!ctx.in.riscvTableJumpSection || !ctx.in.riscvTableJumpSection->isFinalized)
+    return false;
+
+  const auto jalr = sec.contentMaybeDecompress().data()[r.offset + 4];
+  const uint8_t rd = extractBits(jalr, 11, 7);
+  int tblEntryIndex = -1;
+  if (rd == 0) {
+    tblEntryIndex = ctx.in.riscvTableJumpSection->getCMJTEntryIndex(r.sym);
+  } else if (rd == X_RA) {
+    tblEntryIndex = ctx.in.riscvTableJumpSection->getCMJALTEntryIndex(r.sym);
+  }
+
+  if (tblEntryIndex >= 0) {
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_TBJAL;
+    sec.relaxAux->writes.push_back(0xA002 |
+                                   (tblEntryIndex << 2)); // cm.jt or cm.jalt
+    remove = 6;
+    return true;
+  }
+  return false;
+}
+
 // Relax R_RISCV_CALL/R_RISCV_CALL_PLT auipc+jalr to c.j, c.jal, or jal.
 static void relaxCall(Ctx &ctx, const InputSection &sec, size_t i, uint64_t loc,
                       Relocation &r, uint32_t &remove) {
@@ -1149,6 +1194,8 @@ void RISCV::finalizeRelax(int passes) const {
             skip = 4;
             write32le(p, aux.writes[writesIdx++]);
             break;
+          case R_RISCV_64:
+            break;
           case R_RISCV_32:
             // Used by relaxTlsLe to write a uint32_t then suppress the handling
             // in relocateAlloc.
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index fd57967a1d21f..58ec6919fb82b 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -67,6 +67,7 @@ class MipsGotSection;
 class MipsRldMapSection;
 class PPC32Got2Section;
 class PPC64LongBranchTargetSection;
+class TableJumpSection;
 class PltSection;
 class RelocationBaseSection;
 class RelroPaddingSection;
@@ -369,6 +370,7 @@ struct Config {
   bool resolveGroups;
   bool relrGlibc = false;
   bool relrPackDynRelocs = false;
+  bool relaxTbljal;
   llvm::DenseSet<llvm::StringRef> saveTempsArgs;
   llvm::SmallVector<std::pair<llvm::GlobPattern, uint32_t>, 0> shuffleSections;
   bool singleRoRx;
@@ -581,6 +583,7 @@ struct InStruct {
   std::unique_ptr<RelroPaddingSection> relroPadding;
   std::unique_ptr<SyntheticSection> armCmseSGSection;
   std::unique_ptr<PPC64LongBranchTargetSection> ppc64LongBranchTarget;
+  std::unique_ptr<TableJumpSection> riscvTableJumpSection;
   std::unique_ptr<SyntheticSection> mipsAbiFlags;
   std::unique_ptr<MipsGotSection> mipsGot;
   std::unique_ptr<SyntheticSection> mipsOptions;
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index 0d6dda4b60d3a..3a83f6c36a91c 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -378,6 +378,9 @@ defm use_android_relr_tags: BB<"use-android-relr-tags",
     "Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*",
     "Use SHT_RELR / DT_RELR* tags (default)">;
 
+def relax_tbljal: FF<"relax-tbljal">,
+  HelpText<"Enable conversion of call instructions to table jump instruction from the Zcmt extension for frequently called functions (RISC-V only)">;
+
 def pic_veneer: F<"pic-veneer">,
   HelpText<"Always generate position independent thunks (veneers)">;
 
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index ac3ec63f0a7a5..fcf9139b4011d 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -380,6 +380,51 @@ class GotPltSection final : public SyntheticSection {
   SmallVector<const Symbol *, 0> entries;
 };
 
+class TableJumpSection final : public SyntheticSection {
+public:
+  TableJumpSection(Ctx &);
+  size_t getSize() const override;
+  void writeTo(uint8_t *buf) override;
+  void finalizeContents() override;
+
+  int32_t getSizeReduction();
+  void addCMJTEntryCandidate(const Symbol *symbol, int csReduction);
+  int getCMJTEntryIndex(const Symbol *symbol);
+  void addCMJALTEntryCandidate(const Symbol *symbol, int csReduction);
+  int getCMJALTEntryIndex(const Symbol *symbol);
+  void scanTableJumpEntries(const InputSection &sec) const;
+
+  bool isFinalized = false;
+
+private:
+  SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>, 0>
+  finalizeEntry(llvm::DenseMap<const Symbol *, int> EntryMap, uint32_t maxSize);
+  void addEntry(const Symbol *symbol,
+                llvm::DenseMap<const Symbol *, int> &entriesList,
+                int csReduction);
+  uint32_t getIndex(const Symbol *symbol, uint32_t maxSize,
+                    SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>,
+                                0> &entriesList);
+  void writeEntries(uint8_t *buf,
+                    SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>,
+                                0> &entriesList);
+  void padWords(uint8_t *buf, const uint8_t maxWordCount);
+
+  // used in finalizeContents function.
+  static const size_t maxCMJTEntrySize = 32;
+  static const size_t maxCMJALTEntrySize = 224;
+
+  static const size_t startCMJTEntryIdx = 0;
+  static const size_t startCMJALTEntryIdx = 32;
+
+  llvm::DenseMap<const Symbol *, int> CMJTEntryCandidates;
+  SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>, 0>
+      finalizedCMJTEntries;
+  llvm::DenseMap<const Symbol *, int> CMJALTEntryCandidates;
+  SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>, 0>
+      finalizedCMJALTEntries;
+};
+
 // The IgotPltSection is a Got associated with the PltSection for GNU Ifunc
 // Symbols that will be relocated by Target->IRelativeRel.
 // On most Targets the IgotPltSection will immediately follow the GotPltSection
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 9f0605138a4fb..0882b1f5ca2c4 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -37,6 +37,9 @@ class TargetInfo {
   virtual void writeGotPltHeader(uint8_t *buf) const {}
   virtual void writeGotHeader(uint8_t *buf) const {}
   virtual void writeGotPlt(uint8_t *buf, const Symbol &s) const {};
+  virtual void writeTableJumpHeader(uint8_t *buf) const {};
+  virtual void writeTableJumpEntry(uint8_t *buf,
+                                   const uint64_t symbol) const {};
   virtual void writeIgotPlt(uint8_t *buf, const Symbol &s) const {}
   virtual int64_t getImplicitAddend(const uint8_t *buf, RelType type) const;
   virtual int getTlsGdRelaxSkip(RelType type) const { return 1; }
diff --git a/lld/test/ELF/riscv-no-tbljal-call.s b/lld/test/ELF/riscv-no-tbljal-call.s
new file mode 100644
index 0000000000000..61d1d87d11057
--- /dev/null
+++ b/lld/test/ELF/riscv-no-tbljal-call.s
@@ -0,0 +1,33 @@
+# REQUIRES: riscv
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax -mattr=zcmt %s -o %t.rv32.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax -mattr=zcmt %s -o %t.rv64.o
+
+# tbljal conversion
+# RUN: ld.lld %t.rv32.o --relax-tbljal --defsym foo=0x150000 -o %t.rv32
+# RUN: ld.lld %t.rv64.o --relax-tbljal --defsym foo=0x150000 -o %t.rv64
+
+# jump table
+# RUN: llvm-objdump -h %t.rv32 | FileCheck --check-prefix=JUMPTABLE32 %s
+# RUN: llvm-objdump -h %t.rv64 | FileCheck --check-prefix=JUMPTABLE64 %s
+
+# JUMPTABLE32:  2 .riscv.jvt    00000000 00011100 TEXT
+# JUMPTABLE64:  2 .riscv.jvt    00000000 0000000000011140 TEXT
+
+.global _start
+.p2align 3
+_start:
+  call foo
+  tail foo_1
+  tail foo_2
+  tail foo_3
+
+foo_1:
+  nop
+
+foo_2:
+  nop
+
+foo_3:
+  nop
+
diff --git a/lld/test/ELF/riscv-tbljal-call.s b/lld/test/ELF/riscv-tbljal-call.s
new file mode 100644
index 0000000000000..1e13a328236f3
--- /dev/null
+++ b/lld/test/ELF/riscv-tbljal-call.s
@@ -0,0 +1,109 @@
+# REQUIRES: riscv
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax -mattr=zcmt %s -o %t.rv32.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax -mattr=zcmt %s -o %t.rv64.o
+
+# tbljal conversion
+# RUN: ld.lld %t.rv32.o --relax-tbljal --defsym foo=0x150000 --defsym foo_1=0x150010 --defsym foo_3=0x150030 -o %t.rv32
+# RUN: ld.lld %t.rv64.o --relax-tbljal --defsym foo=0x150000 --defsym foo_1=0x150010 --defsym foo_3=0x150030 -o %t.rv64
+# RUN: llvm-objdump -d -M no-aliases --mattr=zcmt --no-show-raw-insn %t.rv32 | FileCheck --check-prefix=TBLJAL32 %s
+# RUN: llvm-objdump -d -M no-aliases --mattr=zcmt --no-show-raw-insn %t.rv64 | FileCheck --check-prefix=TBLJAL64 %s
+
+# jump table
+# RUN: llvm-objdump -j .riscv.jvt -s %t.rv32 | FileCheck --check-prefix=JUMPTABLE32 %s
+# RUN: llvm-objdump -j .riscv.jvt -s %t.rv64 | FileCheck --check-prefix=JUMPTABLE64 %s
+
+# TBLJAL32:      cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jalt 0x20
+# TBLJAL32-NEXT: cm.jt   0x2
+# TBLJAL32-NEXT: cm.jt   0x1
+# TBLJAL32-NEXT: cm.jt   0x1
+# TBLJAL32-NEXT: cm.jt   0x1
+# TBLJAL32-NEXT: cm.jt   0x0
+# TBLJAL32-NEXT: c.j     0x110f6 <foo_2>
+# TBLJAL32-NEXT: cm.jt   0x0
+# TBLJAL32-NEXT: cm.jt   0x0
+# TBLJAL32-NEXT: cm.jt   0x0
+# TBLJAL32-NEXT: cm.jt   0x0
+
+# TBLJAL64:      cm.jt   0x1
+# TBLJAL64-NEXT: cm.jt   0x1
+# TBLJAL64-NEXT: cm.jt   0x1
+# TBLJAL64-NEXT: cm.jt   0x0
+# TBLJAL64-NEXT: c.j     0x111e2 <foo_2>
+# TBLJAL64-NEXT: cm.jt   0x0
+# TBLJAL64-NEXT: cm.jt   0x0
+# TBLJAL64-NEXT: cm.jt   0x0
+# TBLJAL64-NEXT: cm.jt   0x0
+
+
+# JUMPTABLE32:      30001500 10001500 00001500 00000000
+# JUMPTABLE32-NEXT: 00000000 00000000 00000000 00000000
+# JUMPTABLE32-NEXT: 00000000 00000000 00000000 00000000
+# JUMPTABLE32-NEXT: 00000000 00000000 00000000 00000000
+# JUMPTABLE32-NEXT: 00000000 00000000 00000000 00000000
+# JUMPTABLE32-NEXT: 00000000 00000000 00000000 00000000
+# JUMPTABLE32-NEXT: 00000000 00000000 00000000 00000000
+# JUMPTABLE32-NEXT: 00000000 00000000 00000000 00000000
+# JUMPTABLE32-NEXT: 00001500
+
+# JUMPTABLE64:      30001500 00000000 10001500 00000000
+
+.global _start
+.p2align 3
+_start:
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  call foo
+  tail foo
+  tail foo_1
+  tail foo_1
+  tail foo_1
+  tail foo_3
+  tail foo_2
+  tail foo_3
+  tail foo_3
+  tail foo_3
+  tail foo_3
+
+foo_2:
+  nop
+
+
diff --git a/lld/test/ELF/riscv-tbljal-syms.s b/lld/test/ELF/riscv-tbljal-syms.s
new file mode 100644
index 0000000000000..2c9955c64ec32
--- /dev/null
+++ b/lld/test/ELF/riscv-tbljal-syms.s
@@ -0,0 +1,42 @@
+# REQUIRES: riscv
+
+// Check that relaxation correctly adjusts symbol addresses and sizes.
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax -mattr=zcmt %s -o %t.rv32.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax -mattr=zcmt %s -o %t.rv64.o
+# RUN: ld.lld -Ttext=0x100000 --relax-tbljal %t.rv32.o -o %t.rv32
+# RUN: ld.lld -Ttext=0x100000 --relax-tbljal %t.rv64.o -o %t.rv64
+
+# RUN: llvm-readelf -s %t.rv32 | FileCheck --check-prefix=CHECK32 %s
+# RUN: llvm-readelf -s %t.rv64 | FileCheck --check-prefix=CHECK64 %s
+
+# CHECK32:      00100000     4 NOTYPE  LOCAL  DEFAULT     1 a
+# CHECK32-NEXT: 00100000     6 NOTYPE  LOCAL  DEFAULT     1 b
+# CHECK32-NEXT: 00100000     0 NOTYPE  LOCAL  DEFAULT     1 $x
+# CHECK32-NEXT: 00100004     2 NOTYPE  LOCAL  DEFAULT     1 c
+# CHECK32-NEXT: 00100004     6 NOTYPE  LOCAL  DEFAULT     1 d
+# CHECK32-NEXT: 00100000    10 NOTYPE  GLOBAL DEFAULT     1 _start
+# CHECK32-NEXT: 00100040     0 NOTYPE  GLOBAL DEFAULT     2 __jvt_base$
+
+# CHECK64:      00100000     4 NOTYPE  LOCAL  DEFAULT     1 a
+# CHECK64-NEXT: 00100000     8 NOTYPE  LOCAL  DEFAULT     1 b
+# CHECK64-NEXT: 00100000     0 NOTYPE  LOCAL  DEFAULT     1 $x
+# CHECK64-NEXT: 00100004     4 NOTYPE  LOCAL  DEFAULT     1 c
+# CHECK64-NEXT: 00100004     8 NOTYPE  LOCAL  DEFAULT     1 d
+# CHECK64-NEXT: 00100000    12 NOTYPE  GLOBAL DEFAULT     1 _start
+# CHECK64-NEXT: 00100040     0 NOTYPE  GLOBAL DEFAULT     2 __jvt_base$
+
+.global _start
+_start:
+a:
+b:
+  add  a0, a1, a2
+.size a, . - a
+c:
+d:
+  call _start
+.size b, . - b
+.size c, . - c
+  add a0, a1, a2
+.size d, . - d
+.size _start, . - _start

>From 4a8274a2e686e9683eddd4de908ea2928ba3f26f Mon Sep 17 00:00:00 2001
From: Robin Kastberg <robin.kastberg at iar.com>
Date: Mon, 13 Oct 2025 15:51:15 +0200
Subject: [PATCH 2/8] clang-format

---
 lld/ELF/Arch/RISCV.cpp | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 49aadfba00584..098c62bcf40f3 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -72,10 +72,10 @@ class RISCV final : public TargetInfo {
 #define INTERNAL_R_RISCV_GPREL_S 257
 #define INTERNAL_R_RISCV_X0REL_I 258
 #define INTERNAL_R_RISCV_X0REL_S 259
-#define INTERNAL_R_RISCV_TBJAL   260
+#define INTERNAL_R_RISCV_TBJAL 260
 
 const uint64_t dtpOffset = 0x800;
-const uint32_t jvtAlign  = 64;
+const uint32_t jvtAlign = 64;
 
 namespace {
 enum Op {
@@ -760,9 +760,10 @@ void elf::initSymbolAnchors(Ctx &ctx) {
   }
 }
 
-static bool relaxTableJump(Ctx &ctx, const InputSection &sec, size_t i, uint64_t loc,
-                           Relocation &r, uint32_t &remove) {
-  if (!ctx.in.riscvTableJumpSection || !ctx.in.riscvTableJumpSection->isFinalized)
+static bool relaxTableJump(Ctx &ctx, const InputSection &sec, size_t i,
+                           uint64_t loc, Relocation &r, uint32_t &remove) {
+  if (!ctx.in.riscvTableJumpSection ||
+      !ctx.in.riscvTableJumpSection->isFinalized)
     return false;
 
   const auto jalr = sec.contentMaybeDecompress().data()[r.offset + 4];

>From 538d001b124245715530f4db25041cd53186e6ae Mon Sep 17 00:00:00 2001
From: Robin Kastberg <robin.kastberg at iar.com>
Date: Mon, 13 Oct 2025 16:39:56 +0200
Subject: [PATCH 3/8] right version of clang-format helps

---
 lld/ELF/Target.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 0882b1f5ca2c4..ebb8eae2fea1c 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -38,8 +38,8 @@ class TargetInfo {
   virtual void writeGotHeader(uint8_t *buf) const {}
   virtual void writeGotPlt(uint8_t *buf, const Symbol &s) const {};
   virtual void writeTableJumpHeader(uint8_t *buf) const {};
-  virtual void writeTableJumpEntry(uint8_t *buf,
-                                   const uint64_t symbol) const {};
+  virtual void writeTableJumpEntry(uint8_t *buf, const uint64_t symbol) const {
+  };
   virtual void writeIgotPlt(uint8_t *buf, const Symbol &s) const {}
   virtual int64_t getImplicitAddend(const uint8_t *buf, RelType type) const;
   virtual int getTlsGdRelaxSkip(RelType type) const { return 1; }

>From 1ca85487af3000b9c2180e04edb88f97da794395 Mon Sep 17 00:00:00 2001
From: Robin Kastberg <Robin.Kastberg at iar.com>
Date: Tue, 14 Oct 2025 11:30:58 +0200
Subject: [PATCH 4/8] i lost a commit somehow

---
 lld/ELF/Arch/RISCV.cpp        | 221 ++++++++++++++++++++++++++++++++++
 lld/ELF/SyntheticSections.cpp |  10 ++
 lld/ELF/Writer.cpp            |  13 ++
 3 files changed, 244 insertions(+)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 098c62bcf40f3..7f662feca5645 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -807,6 +807,8 @@ static void relaxCall(Ctx &ctx, const InputSection &sec, size_t i, uint64_t loc,
     sec.relaxAux->relocTypes[i] = R_RISCV_RVC_JUMP;
     sec.relaxAux->writes.push_back(0x2001); // c.jal
     remove = 6;
+  } else if (remove >= 6 && relaxTableJump(ctx, sec, i, loc, r, remove)) {
+    // relaxTableJump sets remove
   } else if (remove >= 4 && isInt<21>(displace)) {
     sec.relaxAux->relocTypes[i] = R_RISCV_JAL;
     sec.relaxAux->writes.push_back(0x6f | rd << 7); // jal
@@ -1184,6 +1186,12 @@ void RISCV::finalizeRelax(int passes) const {
           case INTERNAL_R_RISCV_X0REL_I:
           case INTERNAL_R_RISCV_X0REL_S:
             break;
+          case INTERNAL_R_RISCV_TBJAL:
+            assert(ctx.arg.relaxTbljal);
+            assert((aux.writes[writesIdx] & 0xfc03) == 0xA002);
+            skip = 2;
+            write16le(p, aux.writes[writesIdx++]);
+            break;
           case R_RISCV_RELAX:
             // Used by relaxTlsLe to indicate the relocation is ignored.
             break;
@@ -1524,3 +1532,216 @@ void elf::mergeRISCVAttributesSections(Ctx &ctx) {
 }
 
 void elf::setRISCVTargetInfo(Ctx &ctx) { ctx.target.reset(new RISCV(ctx)); }
+
+TableJumpSection::TableJumpSection(Ctx &ctx)
+    : SyntheticSection(ctx, ".riscv.jvt", SHT_PROGBITS,
+                       SHF_ALLOC | SHF_EXECINSTR, jvtAlign) {}
+
+void TableJumpSection::addCMJTEntryCandidate(const Symbol *symbol,
+                                             int csReduction) {
+  addEntry(symbol, CMJTEntryCandidates, csReduction);
+}
+
+int TableJumpSection::getCMJTEntryIndex(const Symbol *symbol) {
+  uint32_t index = getIndex(symbol, maxCMJTEntrySize, finalizedCMJTEntries);
+  return index < finalizedCMJTEntries.size() ? (int)(startCMJTEntryIdx + index)
+                                             : -1;
+}
+
+void TableJumpSection::addCMJALTEntryCandidate(const Symbol *symbol,
+                                               int csReduction) {
+  addEntry(symbol, CMJALTEntryCandidates, csReduction);
+}
+
+int TableJumpSection::getCMJALTEntryIndex(const Symbol *symbol) {
+  uint32_t index = getIndex(symbol, maxCMJALTEntrySize, finalizedCMJALTEntries);
+  return index < finalizedCMJALTEntries.size()
+             ? (int)(startCMJALTEntryIdx + index)
+             : -1;
+}
+
+void TableJumpSection::addEntry(
+    const Symbol *symbol, llvm::DenseMap<const Symbol *, int> &entriesList,
+    int csReduction) {
+  entriesList[symbol] += csReduction;
+}
+
+uint32_t TableJumpSection::getIndex(
+    const Symbol *symbol, uint32_t maxSize,
+    SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>, 0>
+        &entriesList) {
+  // Find this symbol in the ordered list of entries if it exists.
+  assert(maxSize >= entriesList.size() &&
+         "Finalized vector of entries exceeds maximum");
+  auto idx = std::find_if(
+      entriesList.begin(), entriesList.end(),
+      [symbol](llvm::detail::DenseMapPair<const Symbol *, int> &e) {
+        return e.first == symbol;
+      });
+
+  if (idx == entriesList.end())
+    return entriesList.size();
+  return idx - entriesList.begin();
+}
+
+void TableJumpSection::scanTableJumpEntries(const InputSection &sec) const {
+  for (auto [i, r] : llvm::enumerate(sec.relocations)) {
+    Defined *definedSymbol = dyn_cast<Defined>(r.sym);
+    if (!definedSymbol)
+      continue;
+    if (i + 1 == sec.relocs().size() ||
+        sec.relocs()[i + 1].type != R_RISCV_RELAX)
+      continue;
+    switch (r.type) {
+    case R_RISCV_JAL:
+    case R_RISCV_CALL:
+    case R_RISCV_CALL_PLT: {
+      const auto jalr = sec.contentMaybeDecompress().data()[r.offset + 4];
+      const uint8_t rd = extractBits(jalr, 11, 7);
+
+      int csReduction = 6;
+      if (sec.relaxAux->relocTypes[i] == R_RISCV_RVC_JUMP)
+        continue;
+      else if (sec.relaxAux->relocTypes[i] == R_RISCV_JAL)
+        csReduction = 2;
+
+      if (rd == 0)
+        ctx.in.riscvTableJumpSection->addCMJTEntryCandidate(r.sym, csReduction);
+      else if (rd == X_RA)
+        ctx.in.riscvTableJumpSection->addCMJALTEntryCandidate(r.sym,
+                                                              csReduction);
+    }
+    }
+  }
+}
+
+void TableJumpSection::finalizeContents() {
+  if (isFinalized)
+    return;
+  isFinalized = true;
+
+  finalizedCMJTEntries = finalizeEntry(CMJTEntryCandidates, maxCMJTEntrySize);
+  CMJTEntryCandidates.clear();
+  int32_t CMJTSizeReduction = getSizeReduction();
+  finalizedCMJALTEntries =
+      finalizeEntry(CMJALTEntryCandidates, maxCMJALTEntrySize);
+  CMJALTEntryCandidates.clear();
+
+  if (finalizedCMJALTEntries.size() > 0 &&
+      getSizeReduction() < CMJTSizeReduction) {
+    // Stop relax to cm.jalt if there will be the code reduction of cm.jalt is
+    // smaller then the size of padding 0 for doing cm.jalt optmise
+    finalizedCMJALTEntries.clear();
+  }
+  // if table jump still got negative effect, give up.
+  if (getSizeReduction() <= 0) {
+    warn("Table Jump Relaxation didn't got any reduction for code size.");
+    finalizedCMJTEntries.clear();
+  }
+}
+
+// Sort the map in decreasing order of the amount of code reduction provided
+// by the entries. Drop any entries that can't fit in the map from the tail
+// end since they provide less code reduction. Drop any entries that cause
+// an increase in code size (i.e. the reduction from instruction conversion
+// does not cover the code size gain from adding a table entry).
+SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>, 0>
+TableJumpSection::finalizeEntry(llvm::DenseMap<const Symbol *, int> EntryMap,
+                                uint32_t maxSize) {
+  auto cmp = [](const llvm::detail::DenseMapPair<const Symbol *, int> &p1,
+                const llvm::detail::DenseMapPair<const Symbol *, int> &p2) {
+    return p1.second > p2.second;
+  };
+
+  SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>, 0>
+      tempEntryVector;
+  std::copy(EntryMap.begin(), EntryMap.end(),
+            std::back_inserter(tempEntryVector));
+  std::sort(tempEntryVector.begin(), tempEntryVector.end(), cmp);
+
+  auto finalizedVector = tempEntryVector;
+  if (tempEntryVector.size() >= maxSize)
+    finalizedVector =
+        SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>, 0>(
+            tempEntryVector.begin(), tempEntryVector.begin() + maxSize);
+
+  // Drop any items that have a negative effect (i.e. increase code size).
+  while (finalizedVector.size()) {
+    if (finalizedVector.rbegin()->second < ctx.arg.wordsize)
+      finalizedVector.pop_back();
+    else
+      break;
+  }
+  return finalizedVector;
+}
+
+size_t TableJumpSection::getSize() const {
+  if (isFinalized) {
+    if (!finalizedCMJALTEntries.empty())
+      return (startCMJALTEntryIdx + finalizedCMJALTEntries.size()) *
+             ctx.arg.wordsize;
+    return (startCMJTEntryIdx + finalizedCMJTEntries.size()) * ctx.arg.wordsize;
+  } else {
+    if (!CMJALTEntryCandidates.empty())
+      return (startCMJALTEntryIdx + CMJALTEntryCandidates.size()) *
+             ctx.arg.wordsize;
+    return (startCMJTEntryIdx + CMJTEntryCandidates.size()) * ctx.arg.wordsize;
+  }
+}
+
+int32_t TableJumpSection::getSizeReduction() {
+  // The total reduction in code size is J + JA - JTS - JAE.
+  // Where:
+  // J = number of bytes saved for all the cm.jt instructions emitted
+  // JA = number of bytes saved for all the cm.jalt instructions emitted
+  // JTS = size of the part of the table for cm.jt jumps (i.e. 32 x wordsize)
+  // JAE = number of entries emitted for the cm.jalt jumps x wordsize
+
+  int32_t sizeReduction = -getSize();
+  for (auto entry : finalizedCMJTEntries) {
+    sizeReduction += entry.second;
+  }
+  for (auto entry : finalizedCMJALTEntries) {
+    sizeReduction += entry.second;
+  }
+  return sizeReduction;
+}
+
+void TableJumpSection::writeTo(uint8_t *buf) {
+  if (getSizeReduction() <= 0)
+    return;
+  ctx.target->writeTableJumpHeader(buf);
+  writeEntries(buf + startCMJTEntryIdx * ctx.arg.wordsize,
+               finalizedCMJTEntries);
+  if (finalizedCMJALTEntries.size() > 0) {
+    padWords(buf + ((startCMJTEntryIdx + finalizedCMJTEntries.size()) *
+                    ctx.arg.wordsize),
+             startCMJALTEntryIdx);
+    writeEntries(buf + (startCMJALTEntryIdx * ctx.arg.wordsize),
+                 finalizedCMJALTEntries);
+  }
+}
+
+void TableJumpSection::padWords(uint8_t *buf, const uint8_t maxWordCount) {
+  for (size_t i = 0; i < maxWordCount; ++i) {
+    if (ctx.arg.is64)
+      write64le(buf + i, 0);
+    else
+      write32le(buf + i, 0);
+  }
+}
+
+void TableJumpSection::writeEntries(
+    uint8_t *buf,
+    SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>, 0>
+        &entriesList) {
+  for (const auto &entry : entriesList) {
+    assert(entry.second > 0);
+    // Use the symbol from in.symTab to ensure we have the final adjusted
+    // symbol.
+    if (!entry.first->isDefined())
+      continue;
+    ctx.target->writeTableJumpEntry(buf, entry.first->getVA(ctx, 0));
+    buf += ctx.arg.wordsize;
+  }
+}
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index bbf4b29a9fda5..e65e1f2c19048 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -4872,6 +4872,16 @@ template <class ELFT> void elf::createSyntheticSections(Ctx &ctx) {
     add(*ctx.in.ppc64LongBranchTarget);
   }
 
+    if (ctx.arg.emachine == EM_RISCV && ctx.arg.relaxTbljal) {
+    ctx.in.riscvTableJumpSection = std::make_unique<TableJumpSection>(ctx);
+    add(*ctx.in.riscvTableJumpSection);
+
+    Symbol *s = ctx.symtab->addSymbol(Defined{ctx,
+        /*file=*/nullptr, "__jvt_base$", STB_GLOBAL, STT_NOTYPE, STT_NOTYPE,
+        /*value=*/0, /*size=*/0, ctx.in.riscvTableJumpSection.get()});
+    s->isUsedInRegularObj = true;
+  }
+
   ctx.in.gotPlt = std::make_unique<GotPltSection>(ctx);
   add(*ctx.in.gotPlt);
   ctx.in.igotPlt = std::make_unique<IgotPltSection>(ctx);
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 4fa80397cbfa7..f8558aab7f372 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1570,6 +1570,19 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
       changed |= a32p.createFixes();
     }
 
+    if (ctx.arg.relaxTbljal) {
+      if (!changed) {
+        // scan all R_RISCV_JAL, R_RISCV_CALL/R_RISCV_CALL_PLT for RISCV Zcmt
+        // Jump table.
+        for (InputSectionBase *inputSection : ctx.inputSections) {
+          ctx.in.riscvTableJumpSection->scanTableJumpEntries(
+              cast<InputSection>(*inputSection));
+        }
+        ctx.in.riscvTableJumpSection->finalizeContents();
+        changed |= ctx.target->relaxOnce(pass);
+      }
+    }
+
     finalizeSynthetic(ctx, ctx.in.got.get());
     if (ctx.in.mipsGot)
       ctx.in.mipsGot->updateAllocSize(ctx);

>From 00784b93bea681f3b601fbd69ba7a5c60ca7e0f9 Mon Sep 17 00:00:00 2001
From: Robin Kastberg <robin.kastberg at iar.com>
Date: Tue, 14 Oct 2025 11:47:45 +0200
Subject: [PATCH 5/8] fix option setting. tests are passing again

---
 lld/ELF/Driver.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 62f7fffce7dbe..9cac927615839 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1621,6 +1621,7 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
   }
   ctx.arg.zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true);
   ctx.arg.zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true);
+  ctx.arg.relaxTbljal = args.hasArg(OPT_relax_tbljal);
   ctx.arg.zForceBti = hasZOption(args, "force-bti");
   ctx.arg.zForceIbt = hasZOption(args, "force-ibt");
   ctx.arg.zZicfilp = getZZicfilp(ctx, args);

>From 914cf2b2f25c1d5ae437ebf8b2ad8920a63a73ba Mon Sep 17 00:00:00 2001
From: Robin Kastberg <robin.kastberg at iar.com>
Date: Tue, 14 Oct 2025 15:10:03 +0200
Subject: [PATCH 6/8] fix instruction read and R_RISCV_JAL handling

---
 lld/ELF/Arch/RISCV.cpp | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 7f662feca5645..0667a046182f9 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -766,7 +766,8 @@ static bool relaxTableJump(Ctx &ctx, const InputSection &sec, size_t i,
       !ctx.in.riscvTableJumpSection->isFinalized)
     return false;
 
-  const auto jalr = sec.contentMaybeDecompress().data()[r.offset + 4];
+  const uint32_t jalr = read32le(sec.contentMaybeDecompress().data() +
+                                 r.offset + (r.type == R_RISCV_JAL ? 0 : 4));
   const uint8_t rd = extractBits(jalr, 11, 7);
   int tblEntryIndex = -1;
   if (rd == 0) {
@@ -779,7 +780,7 @@ static bool relaxTableJump(Ctx &ctx, const InputSection &sec, size_t i,
     sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_TBJAL;
     sec.relaxAux->writes.push_back(0xA002 |
                                    (tblEntryIndex << 2)); // cm.jt or cm.jalt
-    remove = 6;
+    remove = (r.type == R_RISCV_JAL ? 2 : 6);
     return true;
   }
   return false;
@@ -932,6 +933,11 @@ static bool relax(Ctx &ctx, int pass, InputSection &sec) {
         relaxCall(ctx, sec, i, loc, r, remove);
       }
       break;
+    case R_RISCV_JAL:
+      if (relaxable(relocs, i)) {
+        relaxTableJump(ctx, sec, i, loc, r, remove);
+      }
+      break;
     case R_RISCV_TPREL_HI20:
     case R_RISCV_TPREL_ADD:
     case R_RISCV_TPREL_LO12_I:
@@ -1596,7 +1602,9 @@ void TableJumpSection::scanTableJumpEntries(const InputSection &sec) const {
     case R_RISCV_JAL:
     case R_RISCV_CALL:
     case R_RISCV_CALL_PLT: {
-      const auto jalr = sec.contentMaybeDecompress().data()[r.offset + 4];
+      const uint32_t jalr =
+          read32le(sec.contentMaybeDecompress().data() + r.offset +
+                   (r.type == R_RISCV_JAL ? 0 : 4));
       const uint8_t rd = extractBits(jalr, 11, 7);
 
       int csReduction = 6;

>From a1b8ee1c876d7f3335f62ffd080da71bb7123888 Mon Sep 17 00:00:00 2001
From: Robin Kastberg <kastberg at gmail.com>
Date: Wed, 15 Oct 2025 08:17:33 +0200
Subject: [PATCH 7/8] Apply suggestions from code review

Co-authored-by: Craig Topper <craig.topper at sifive.com>
---
 lld/ELF/Arch/RISCV.cpp      | 6 +++---
 lld/ELF/SyntheticSections.h | 8 ++++----
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 0667a046182f9..64a7f83adc174 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -770,7 +770,7 @@ static bool relaxTableJump(Ctx &ctx, const InputSection &sec, size_t i,
                                  r.offset + (r.type == R_RISCV_JAL ? 0 : 4));
   const uint8_t rd = extractBits(jalr, 11, 7);
   int tblEntryIndex = -1;
-  if (rd == 0) {
+  if (rd == X_X0) {
     tblEntryIndex = ctx.in.riscvTableJumpSection->getCMJTEntryIndex(r.sym);
   } else if (rd == X_RA) {
     tblEntryIndex = ctx.in.riscvTableJumpSection->getCMJALTEntryIndex(r.sym);
@@ -1635,7 +1635,7 @@ void TableJumpSection::finalizeContents() {
       finalizeEntry(CMJALTEntryCandidates, maxCMJALTEntrySize);
   CMJALTEntryCandidates.clear();
 
-  if (finalizedCMJALTEntries.size() > 0 &&
+  if (!finalizedCMJALTEntries.empty() &&
       getSizeReduction() < CMJTSizeReduction) {
     // Stop relax to cm.jalt if there will be the code reduction of cm.jalt is
     // smaller then the size of padding 0 for doing cm.jalt optmise
@@ -1674,7 +1674,7 @@ TableJumpSection::finalizeEntry(llvm::DenseMap<const Symbol *, int> EntryMap,
             tempEntryVector.begin(), tempEntryVector.begin() + maxSize);
 
   // Drop any items that have a negative effect (i.e. increase code size).
-  while (finalizedVector.size()) {
+  while (!finalizedVector.empty()) {
     if (finalizedVector.rbegin()->second < ctx.arg.wordsize)
       finalizedVector.pop_back();
     else
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index fcf9139b4011d..fefd071c04699 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -411,11 +411,11 @@ class TableJumpSection final : public SyntheticSection {
   void padWords(uint8_t *buf, const uint8_t maxWordCount);
 
   // used in finalizeContents function.
-  static const size_t maxCMJTEntrySize = 32;
-  static const size_t maxCMJALTEntrySize = 224;
+  static constexpr size_t maxCMJTEntrySize = 32;
+  static constexpr size_t maxCMJALTEntrySize = 224;
 
-  static const size_t startCMJTEntryIdx = 0;
-  static const size_t startCMJALTEntryIdx = 32;
+  static constexpr size_t startCMJTEntryIdx = 0;
+  static constexpr size_t startCMJALTEntryIdx = 32;
 
   llvm::DenseMap<const Symbol *, int> CMJTEntryCandidates;
   SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>, 0>

>From 1c7eabf4631408fa7321996931c552902bf46ec4 Mon Sep 17 00:00:00 2001
From: Robin Kastberg <robin.kastberg at iar.com>
Date: Wed, 15 Oct 2025 09:07:08 +0200
Subject: [PATCH 8/8] code review fixes

* move jvtAlign to the rest of the variables
* improve comment
* use resize (with added test)
---
 lld/ELF/Arch/RISCV.cpp                 |  26 +--
 lld/ELF/SyntheticSections.h            |   2 +
 lld/test/ELF/riscv-tbljal-many-jumps.s | 266 +++++++++++++++++++++++++
 3 files changed, 281 insertions(+), 13 deletions(-)
 create mode 100644 lld/test/ELF/riscv-tbljal-many-jumps.s

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 64a7f83adc174..255700368fa2a 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -75,7 +75,6 @@ class RISCV final : public TargetInfo {
 #define INTERNAL_R_RISCV_TBJAL 260
 
 const uint64_t dtpOffset = 0x800;
-const uint32_t jvtAlign = 64;
 
 namespace {
 enum Op {
@@ -1541,7 +1540,7 @@ void elf::setRISCVTargetInfo(Ctx &ctx) { ctx.target.reset(new RISCV(ctx)); }
 
 TableJumpSection::TableJumpSection(Ctx &ctx)
     : SyntheticSection(ctx, ".riscv.jvt", SHT_PROGBITS,
-                       SHF_ALLOC | SHF_EXECINSTR, jvtAlign) {}
+                       SHF_ALLOC | SHF_EXECINSTR, tableAlign) {}
 
 void TableJumpSection::addCMJTEntryCandidate(const Symbol *symbol,
                                              int csReduction) {
@@ -1637,8 +1636,11 @@ void TableJumpSection::finalizeContents() {
 
   if (!finalizedCMJALTEntries.empty() &&
       getSizeReduction() < CMJTSizeReduction) {
-    // Stop relax to cm.jalt if there will be the code reduction of cm.jalt is
-    // smaller then the size of padding 0 for doing cm.jalt optmise
+    // In memory, the cm.jt table occupies the first 0x20 entries.
+    // To be able to use the cm.jalt table which comes afterwards
+    // it is necessary to pad out the cm.jt table.
+    // Remove cm.jalt entries if the code reduction of cm.jalt is
+    // smaller than the size of the padding.
     finalizedCMJALTEntries.clear();
   }
   // if table jump still got negative effect, give up.
@@ -1668,10 +1670,8 @@ TableJumpSection::finalizeEntry(llvm::DenseMap<const Symbol *, int> EntryMap,
   std::sort(tempEntryVector.begin(), tempEntryVector.end(), cmp);
 
   auto finalizedVector = tempEntryVector;
-  if (tempEntryVector.size() >= maxSize)
-    finalizedVector =
-        SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>, 0>(
-            tempEntryVector.begin(), tempEntryVector.begin() + maxSize);
+
+  finalizedVector.resize(maxSize);
 
   // Drop any items that have a negative effect (i.e. increase code size).
   while (!finalizedVector.empty()) {
@@ -1689,12 +1689,12 @@ size_t TableJumpSection::getSize() const {
       return (startCMJALTEntryIdx + finalizedCMJALTEntries.size()) *
              ctx.arg.wordsize;
     return (startCMJTEntryIdx + finalizedCMJTEntries.size()) * ctx.arg.wordsize;
-  } else {
-    if (!CMJALTEntryCandidates.empty())
-      return (startCMJALTEntryIdx + CMJALTEntryCandidates.size()) *
-             ctx.arg.wordsize;
-    return (startCMJTEntryIdx + CMJTEntryCandidates.size()) * ctx.arg.wordsize;
   }
+
+  if (!CMJALTEntryCandidates.empty())
+    return (startCMJALTEntryIdx + CMJALTEntryCandidates.size()) *
+           ctx.arg.wordsize;
+  return (startCMJTEntryIdx + CMJTEntryCandidates.size()) * ctx.arg.wordsize;
 }
 
 int32_t TableJumpSection::getSizeReduction() {
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index fefd071c04699..03b83cf488f47 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -417,6 +417,8 @@ class TableJumpSection final : public SyntheticSection {
   static constexpr size_t startCMJTEntryIdx = 0;
   static constexpr size_t startCMJALTEntryIdx = 32;
 
+  static constexpr size_t tableAlign = 64;
+
   llvm::DenseMap<const Symbol *, int> CMJTEntryCandidates;
   SmallVector<llvm::detail::DenseMapPair<const Symbol *, int>, 0>
       finalizedCMJTEntries;
diff --git a/lld/test/ELF/riscv-tbljal-many-jumps.s b/lld/test/ELF/riscv-tbljal-many-jumps.s
new file mode 100644
index 0000000000000..333d641ca7933
--- /dev/null
+++ b/lld/test/ELF/riscv-tbljal-many-jumps.s
@@ -0,0 +1,266 @@
+# REQUIRES: riscv
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax -mattr=zcmt %s -o %t.rv32.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax -mattr=zcmt %s -o %t.rv64.o
+
+# tbljal conversion
+# RUN: ld.lld %t.rv32.o --relax-tbljal --defsym foo=0x150000 --defsym foo_1=0x150010 --defsym foo_3=0x150030 -o %t.rv32
+# RUN: ld.lld %t.rv64.o --relax-tbljal --defsym foo=0x150000 --defsym foo_1=0x150010 --defsym foo_3=0x150030 -o %t.rv64
+
+# jump table
+# RUN: llvm-objdump -h %t.rv32 | FileCheck --check-prefix=JUMPTABLE32 %s
+# RUN: llvm-objdump -h %t.rv64 | FileCheck --check-prefix=JUMPTABLE64 %s
+
+# JUMPTABLE32:  2 .riscv.jvt    00000080 {{.*}} TEXT
+# JUMPTABLE64:  2 .riscv.jvt    00000100 {{.*}} TEXT
+
+
+.global _start
+.p2align 3
+_start:
+  tail foo
+  tail foo
+  tail foo
+  tail foo
+  tail foo_1
+  tail foo_1
+  tail foo_1
+  tail foo_3
+  tail foo_3
+  tail foo_3
+  tail foo_3
+  tail foo_3
+  tail foo_4
+  tail foo_4
+  tail foo_4
+  tail foo_4
+  tail foo_5
+  tail foo_5
+  tail foo_5
+  tail foo_5
+  tail foo_6
+  tail foo_6
+  tail foo_6
+  tail foo_6
+  tail foo_7
+  tail foo_7
+  tail foo_7
+  tail foo_7
+  tail foo_8
+  tail foo_8
+  tail foo_8
+  tail foo_8
+  tail foo_9
+  tail foo_9
+  tail foo_9
+  tail foo_9
+
+  tail foo_10
+  tail foo_10
+  tail foo_10
+  tail foo_10
+  tail foo_11
+  tail foo_11
+  tail foo_11
+  tail foo_11
+  tail foo_12
+  tail foo_12
+  tail foo_12
+  tail foo_12
+  tail foo_13
+  tail foo_13
+  tail foo_13
+  tail foo_13
+  tail foo_14
+  tail foo_14
+  tail foo_14
+  tail foo_14
+  tail foo_15
+  tail foo_15
+  tail foo_15
+  tail foo_15
+  tail foo_16
+  tail foo_16
+  tail foo_16
+  tail foo_16
+  tail foo_17
+  tail foo_17
+  tail foo_17
+  tail foo_17
+  tail foo_18
+  tail foo_18
+  tail foo_18
+  tail foo_18
+  tail foo_19
+  tail foo_19
+  tail foo_19
+  tail foo_19
+
+  tail foo_20
+  tail foo_20
+  tail foo_20
+  tail foo_20
+  tail foo_21
+  tail foo_21
+  tail foo_21
+  tail foo_21
+  tail foo_22
+  tail foo_22
+  tail foo_22
+  tail foo_22
+  tail foo_23
+  tail foo_23
+  tail foo_23
+  tail foo_23
+  tail foo_24
+  tail foo_24
+  tail foo_24
+  tail foo_24
+  tail foo_25
+  tail foo_25
+  tail foo_25
+  tail foo_25
+  tail foo_26
+  tail foo_26
+  tail foo_26
+  tail foo_26
+  tail foo_27
+  tail foo_27
+  tail foo_27
+  tail foo_27
+  tail foo_28
+  tail foo_28
+  tail foo_28
+  tail foo_28
+  tail foo_29
+  tail foo_29
+  tail foo_29
+  tail foo_29
+
+  tail foo_30
+  tail foo_30
+  tail foo_30
+  tail foo_30
+  tail foo_31
+  tail foo_31
+  tail foo_31
+  tail foo_31
+  tail foo_32
+  tail foo_32
+  tail foo_32
+  tail foo_32
+  tail foo_33
+  tail foo_33
+  tail foo_33
+  tail foo_33
+  tail foo_34
+  tail foo_34
+  tail foo_34
+  tail foo_34
+  tail foo_35
+  tail foo_35
+  tail foo_35
+  tail foo_35
+  tail foo_36
+  tail foo_36
+  tail foo_36
+  tail foo_36
+  tail foo_37
+  tail foo_37
+  tail foo_37
+  tail foo_37
+  tail foo_38
+  tail foo_38
+  tail foo_38
+  tail foo_38
+  tail foo_39
+  tail foo_39
+  tail foo_39
+  tail foo_39
+
+
+.space 16384
+
+foo_3:
+  nop
+
+foo_4:
+  nop
+
+foo_5:
+  nop
+
+foo_6:
+  nop
+
+foo_7:
+  nop
+
+foo_8:
+  nop
+
+foo_9:
+  nop
+
+foo_10:
+  nop
+foo_11:
+  nop
+foo_12:
+  nop
+foo_13:
+  nop
+foo_14:
+  nop
+foo_15:
+  nop
+foo_16:
+  nop
+foo_17:
+  nop
+foo_18:
+  nop
+foo_19:
+  nop
+
+foo_20:
+  nop
+foo_21:
+  nop
+foo_22:
+  nop
+foo_23:
+  nop
+foo_24:
+  nop
+foo_25:
+  nop
+foo_26:
+  nop
+foo_27:
+  nop
+foo_28:
+  nop
+foo_29:
+  nop
+
+foo_30:
+  nop
+foo_31:
+  nop
+foo_32:
+  nop
+foo_33:
+  nop
+foo_34:
+  nop
+foo_35:
+  nop
+foo_36:
+  nop
+foo_37:
+  nop
+foo_38:
+  nop
+foo_39:
+  nop



More information about the llvm-commits mailing list