[lld] [RISCV][LLD] Zcmt RISC-V extension in lld (PR #163142)
Robin Kastberg via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 30 01:57:44 PDT 2025
https://github.com/RobinKastberg updated https://github.com/llvm/llvm-project/pull/163142
>From 8666cb31d77e417912324fb01b530bac357e653a 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] [RISCV][LLD] Zcmt RISC-V extension in lld
This patch implements optimizations for the zcmt extension in lld. A new
TableJumpSection has been added. Scans each R_RISCV_CALL/R_RISCV_CALL_PLT
relocType in each section before the linker relaxation, recording the symbol
In finalizeContents the recorded symbol names are sorted in descending order by
the number of jumps. The top symbols are compressed to table jumps during the
relax process.
This is a continuation of PR #77884
Co-authored-by: Craig Topper <craig.topper at sifive.com>
Co-authored-by: VincentWu <43398706+Xinlong-Wu at users.noreply.github.com>
Co-authored-by: Scott Egerton <9487234+ScottEgerton at users.noreply.github.com>
---
lld/ELF/Arch/RISCV.cpp | 277 +++++++++++++++++++++++++
lld/ELF/Config.h | 3 +
lld/ELF/Driver.cpp | 1 +
lld/ELF/Options.td | 5 +
lld/ELF/SyntheticSections.cpp | 11 +
lld/ELF/SyntheticSections.h | 47 +++++
lld/ELF/Target.h | 3 +
lld/ELF/Writer.cpp | 13 ++
lld/test/ELF/riscv-no-tbljal-call.s | 33 +++
lld/test/ELF/riscv-tbljal-call.s | 109 ++++++++++
lld/test/ELF/riscv-tbljal-many-jumps.s | 266 ++++++++++++++++++++++++
lld/test/ELF/riscv-tbljal-syms.s | 42 ++++
12 files changed, 810 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-many-jumps.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 5ed89e47c672e..2ca82d8c69b1f 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -43,6 +43,8 @@ class RISCV final : public TargetInfo {
void scanSectionImpl(InputSectionBase &, Relocs<RelTy>);
template <class ELFT> void scanSection1(InputSectionBase &);
void scanSection(InputSectionBase &) 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;
@@ -75,6 +77,7 @@ 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;
@@ -274,6 +277,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);
@@ -496,6 +513,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;
@@ -745,6 +765,32 @@ 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 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 == X_X0) {
+ 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 = (r.type == R_RISCV_JAL ? 2 : 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) {
@@ -767,6 +813,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
@@ -890,6 +938,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:
@@ -1144,6 +1197,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;
@@ -1155,6 +1214,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.
@@ -1533,3 +1594,219 @@ template <class ELFT> void RISCV::scanSection1(InputSectionBase &sec) {
void RISCV::scanSection(InputSectionBase &sec) {
invokeELFT(scanSection1, sec);
}
+
+TableJumpSection::TableJumpSection(Ctx &ctx)
+ : SyntheticSection(ctx, ".riscv.jvt", SHT_PROGBITS,
+ SHF_ALLOC | SHF_EXECINSTR, tableAlign) {}
+
+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 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;
+ 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.empty() &&
+ getSizeReduction() < CMJTSizeReduction) {
+ // 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.
+ 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;
+
+ finalizedVector.resize(maxSize);
+
+ // Drop any items that have a negative effect (i.e. increase code size).
+ while (!finalizedVector.empty()) {
+ 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;
+ }
+
+ 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/Config.h b/lld/ELF/Config.h
index 8ec5a2c04e71c..03d5a80ee26f1 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;
@@ -370,6 +371,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;
@@ -582,6 +584,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/Driver.cpp b/lld/ELF/Driver.cpp
index e52d3a0e11113..9dbf9f7af9220 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1623,6 +1623,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);
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index 75184de496448..ea73d9858c25c 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -378,6 +378,11 @@ 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.cpp b/lld/ELF/SyntheticSections.cpp
index bbf4b29a9fda5..2870fd18b6282 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -4872,6 +4872,17 @@ 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/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index ac3ec63f0a7a5..03b83cf488f47 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -380,6 +380,53 @@ 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 constexpr size_t maxCMJTEntrySize = 32;
+ static constexpr size_t maxCMJALTEntrySize = 224;
+
+ 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;
+ 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 90d8ddfede2c0..1b4aad2a66108 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -38,6 +38,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/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);
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-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
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
More information about the llvm-commits
mailing list