diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index dc2ab97e9d9be..255700368fa2a 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,6 +72,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; @@ -269,6 +272,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(R_RISCV_NONE); @@ -490,6 +507,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 +759,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) { @@ -761,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 @@ -884,6 +932,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: @@ -1138,6 +1191,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; @@ -1149,6 +1208,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. @@ -1476,3 +1537,219 @@ 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, 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 &entriesList, + int csReduction) { + entriesList[symbol] += csReduction; +} + +uint32_t TableJumpSection::getIndex( + const Symbol *symbol, uint32_t maxSize, + SmallVector, 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 &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(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, 0> +TableJumpSection::finalizeEntry(llvm::DenseMap EntryMap, + uint32_t maxSize) { + auto cmp = [](const llvm::detail::DenseMapPair &p1, + const llvm::detail::DenseMapPair &p2) { + return p1.second > p2.second; + }; + + SmallVector, 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, 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 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 saveTempsArgs; llvm::SmallVector, 0> shuffleSections; bool singleRoRx; @@ -581,6 +583,7 @@ struct InStruct { std::unique_ptr relroPadding; std::unique_ptr armCmseSGSection; std::unique_ptr ppc64LongBranchTarget; + std::unique_ptr riscvTableJumpSection; std::unique_ptr mipsAbiFlags; std::unique_ptr mipsGot; std::unique_ptr mipsOptions; 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); 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.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 void elf::createSyntheticSections(Ctx &ctx) { add(*ctx.in.ppc64LongBranchTarget); } + if (ctx.arg.emachine == EM_RISCV && ctx.arg.relaxTbljal) { + ctx.in.riscvTableJumpSection = std::make_unique(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(ctx); add(*ctx.in.gotPlt); ctx.in.igotPlt = std::make_unique(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 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, 0> + finalizeEntry(llvm::DenseMap EntryMap, uint32_t maxSize); + void addEntry(const Symbol *symbol, + llvm::DenseMap &entriesList, + int csReduction); + uint32_t getIndex(const Symbol *symbol, uint32_t maxSize, + SmallVector, + 0> &entriesList); + void writeEntries(uint8_t *buf, + SmallVector, + 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 CMJTEntryCandidates; + SmallVector, 0> + finalizedCMJTEntries; + llvm::DenseMap CMJALTEntryCandidates; + SmallVector, 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..ebb8eae2fea1c 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/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 void Writer::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)); + } + 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 +# 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 +# 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