diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp index 04ddb4682917b4b2a3ae3fc12830c449bcc013d3..160fab4aeba9a8da8ed95add75e401d49b701d2d 100644 --- a/lld/ELF/Arch/LoongArch.cpp +++ b/lld/ELF/Arch/LoongArch.cpp @@ -11,6 +11,7 @@ #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" +#include "llvm/Support/LEB128.h" using namespace llvm; using namespace llvm::object; @@ -36,6 +37,8 @@ public: bool usesOnlyLowPageBits(RelType type) const override; void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; + bool relaxOnce(int pass) const override; + void finalizeRelax(int passes) const override; }; } // end anonymous namespace @@ -208,6 +211,16 @@ static bool isJirl(uint32_t insn) { return (insn & 0xfc000000) == JIRL; } +static void handleUleb128(uint8_t *loc, uint64_t val) { + const uint32_t maxcount = 1 + 64 / 7; + uint32_t count; + uint64_t orig = decodeULEB128(loc, &count); + if (count > maxcount) + errorOrWarn(getErrorLocation(loc) + "extra space for uleb128"); + uint64_t mask = count < maxcount ? (1ULL << 7 * count) - 1 : -1ULL; + encodeULEB128((orig + val) & mask, loc, count); +} + LoongArch::LoongArch() { // The LoongArch ISA itself does not have a limit on page sizes. According to // the ISA manual, the PS (page size) field in MTLB entries and CSR.STLBPS is @@ -444,14 +457,18 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s, case R_LARCH_TLS_LE64_LO20: case R_LARCH_TLS_LE64_HI12: return R_TPREL; + case R_LARCH_ADD6: case R_LARCH_ADD8: case R_LARCH_ADD16: case R_LARCH_ADD32: case R_LARCH_ADD64: + case R_LARCH_ADD_ULEB128: + case R_LARCH_SUB6: case R_LARCH_SUB8: case R_LARCH_SUB16: case R_LARCH_SUB32: case R_LARCH_SUB64: + case R_LARCH_SUB_ULEB128: // The LoongArch add/sub relocs behave like the RISCV counterparts; reuse // the RelExpr to avoid code duplication. return R_RISCV_ADD; @@ -519,8 +536,9 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s, case R_LARCH_TLS_GD_HI20: return R_TLSGD_GOT; case R_LARCH_RELAX: - // LoongArch linker relaxation is not implemented yet. - return R_NONE; + return config->relax ? R_RELAX_HINT : R_NONE; + case R_LARCH_ALIGN: + return R_RELAX_HINT; // Other known relocs that are explicitly unimplemented: // @@ -650,6 +668,9 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel, write32le(loc, setK12(read32le(loc), extractBits(val, 63, 52))); return; + case R_LARCH_ADD6: + *loc = (*loc & 0xc0) | ((*loc + val) & 0x3f); + return; case R_LARCH_ADD8: *loc += val; return; @@ -662,6 +683,12 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel, case R_LARCH_ADD64: write64le(loc, read64le(loc) + val); return; + case R_LARCH_ADD_ULEB128: + handleUleb128(loc, val); + return; + case R_LARCH_SUB6: + *loc = (*loc & 0xc0) | ((*loc - val) & 0x3f); + return; case R_LARCH_SUB8: *loc -= val; return; @@ -674,6 +701,9 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel, case R_LARCH_SUB64: write64le(loc, read64le(loc) - val); return; + case R_LARCH_SUB_ULEB128: + handleUleb128(loc, -val); + return; case R_LARCH_MARK_LA: case R_LARCH_MARK_PCREL: @@ -688,6 +718,155 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel, } } +static bool relax(InputSection &sec) { + const uint64_t secAddr = sec.getVA(); + const MutableArrayRef relocs = sec.relocs(); + auto &aux = *sec.relaxAux; + bool changed = false; + ArrayRef sa = ArrayRef(aux.anchors); + uint64_t delta = 0; + + std::fill_n(aux.relocTypes.get(), relocs.size(), R_LARCH_NONE); + aux.writes.clear(); + for (auto [i, r] : llvm::enumerate(relocs)) { + const uint64_t loc = secAddr + r.offset - delta; + uint32_t &cur = aux.relocDeltas[i], remove = 0; + switch (r.type) { + case R_LARCH_ALIGN: { + const uint64_t addend = + r.sym->isUndefined() ? Log2_64(r.addend) + 1 : r.addend; + const uint64_t allBytes = (1 << (addend & 0xff)) - 4; + const uint64_t align = 1 << (addend & 0xff); + const uint64_t maxBytes = addend >> 8; + const uint64_t off = loc & (align - 1); + const uint64_t curBytes = off == 0 ? 0 : align - off; + // All bytes beyond the alignment boundary should be removed. + // If emit bytes more than max bytes to emit, remove all. + if (maxBytes != 0 && curBytes > maxBytes) + remove = allBytes; + else + remove = allBytes - curBytes; + // If we can't satisfy this alignment, we've found a bad input. + if (LLVM_UNLIKELY(static_cast(remove) < 0)) { + errorOrWarn(getErrorLocation((const uint8_t *)loc) + + "insufficient padding bytes for " + lld::toString(r.type) + + ": " + Twine(allBytes) + " bytes available for " + + "requested alignment of " + Twine(align) + " bytes"); + remove = 0; + } + break; + } + } + + // For all anchors whose offsets are <= r.offset, they are preceded by + // the previous relocation whose `relocDeltas` value equals `delta`. + // Decrease their st_value and update their st_size. + for (; sa.size() && sa[0].offset <= r.offset; sa = sa.slice(1)) { + if (sa[0].end) + sa[0].d->size = sa[0].offset - delta - sa[0].d->value; + else + sa[0].d->value = sa[0].offset - delta; + } + delta += remove; + if (delta != cur) { + cur = delta; + changed = true; + } + } + + for (const SymbolAnchor &a : sa) { + if (a.end) + a.d->size = a.offset - delta - a.d->value; + else + a.d->value = a.offset - delta; + } + // Inform assignAddresses that the size has changed. + if (!isUInt<32>(delta)) + fatal("section size decrease is too large: " + Twine(delta)); + sec.bytesDropped = delta; + return changed; +} + +// When relaxing just R_LARCH_ALIGN, relocDeltas is usually changed only once in +// the absence of a linker script. For call and load/store R_LARCH_RELAX, code +// shrinkage may reduce displacement and make more relocations eligible for +// relaxation. Code shrinkage may increase displacement to a call/load/store +// target at a higher fixed address, invalidating an earlier relaxation. Any +// change in section sizes can have cascading effect and require another +// relaxation pass. +bool LoongArch::relaxOnce(int pass) const { + if (config->relocatable) + return false; + + if (pass == 0) + initSymbolAnchors(); + + SmallVector storage; + bool changed = false; + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) + changed |= relax(*sec); + } + return changed; +} + +void LoongArch::finalizeRelax(int passes) const { + log("relaxation passes: " + Twine(passes)); + SmallVector storage; + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) { + RelaxAux &aux = *sec->relaxAux; + if (!aux.relocDeltas) + continue; + + MutableArrayRef rels = sec->relocs(); + ArrayRef old = sec->content(); + size_t newSize = old.size() - aux.relocDeltas[rels.size() - 1]; + uint8_t *p = context().bAlloc.Allocate(newSize); + uint64_t offset = 0; + int64_t delta = 0; + sec->content_ = p; + sec->size = newSize; + sec->bytesDropped = 0; + + // Update section content: remove NOPs for R_LARCH_ALIGN and rewrite + // instructions for relaxed relocations. + for (size_t i = 0, e = rels.size(); i != e; ++i) { + uint32_t remove = aux.relocDeltas[i] - delta; + delta = aux.relocDeltas[i]; + if (remove == 0 && aux.relocTypes[i] == R_LARCH_NONE) + continue; + + // Copy from last location to the current relocated location. + const Relocation &r = rels[i]; + uint64_t size = r.offset - offset; + memcpy(p, old.data() + offset, size); + p += size; + offset = r.offset + remove; + } + memcpy(p, old.data() + offset, old.size() - offset); + + // Subtract the previous relocDeltas value from the relocation offset. + // For a pair of R_LARCH_XXX/R_LARCH_RELAX with the same offset, decrease + // their r_offset by the same delta. + delta = 0; + for (size_t i = 0, e = rels.size(); i != e;) { + uint64_t cur = rels[i].offset; + do { + rels[i].offset -= delta; + if (aux.relocTypes[i] != R_LARCH_NONE) + rels[i].type = aux.relocTypes[i]; + } while (++i != e && rels[i].offset == cur); + delta = aux.relocDeltas[i - 1]; + } + } + } +} + TargetInfo *elf::getLoongArchTargetInfo() { static LoongArch target; return ⌖ diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index d0d75118e30ddd773690a494cf5c2386f37255d6..06120cabc1320d737e6bc1e2ef9cdb61b8ce5d8f 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -44,6 +44,7 @@ public: void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; bool relaxOnce(int pass) const override; + void finalizeRelax(int passes) const override; }; } // end anonymous namespace @@ -513,33 +514,14 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { } } -namespace { -struct SymbolAnchor { - uint64_t offset; - Defined *d; - bool end; // true for the anchor of st_value+st_size -}; -} // namespace - -struct elf::RISCVRelaxAux { - // This records symbol start and end offsets which will be adjusted according - // to the nearest relocDeltas element. - SmallVector anchors; - // For relocations[i], the actual offset is r_offset - (i ? relocDeltas[i-1] : - // 0). - std::unique_ptr relocDeltas; - // For relocations[i], the actual type is relocTypes[i]. - std::unique_ptr relocTypes; - SmallVector writes; -}; -static void initSymbolAnchors() { +void elf::initSymbolAnchors() { SmallVector storage; for (OutputSection *osec : outputSections) { if (!(osec->flags & SHF_EXECINSTR)) continue; for (InputSection *sec : getInputSections(*osec, storage)) { - sec->relaxAux = make(); + sec->relaxAux = make(); if (sec->relocs().size()) { sec->relaxAux->relocDeltas = std::make_unique(sec->relocs().size()); @@ -766,7 +748,7 @@ bool RISCV::relaxOnce(int pass) const { return changed; } -void elf::riscvFinalizeRelax(int passes) { +void RISCV::finalizeRelax(int passes) const { llvm::TimeTraceScope timeScope("Finalize RISC-V relaxation"); log("relaxation passes: " + Twine(passes)); SmallVector storage; @@ -774,7 +756,7 @@ void elf::riscvFinalizeRelax(int passes) { if (!(osec->flags & SHF_EXECINSTR)) continue; for (InputSection *sec : getInputSections(*osec, storage)) { - RISCVRelaxAux &aux = *sec->relaxAux; + RelaxAux &aux = *sec->relaxAux; if (!aux.relocDeltas) continue; diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 2edaa2b4049391eb42a26483cd45877df2754bf1..b178d82407e30703427804f9d6d14016540284f6 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -349,29 +349,62 @@ InputSectionBase *InputSection::getRelocatedSection() const { return sections[info]; } +template +void InputSection::copyRelocations(uint8_t *buf) { + if (config->relax && !config->relocatable && + (config->emachine == EM_RISCV || config->emachine == EM_LOONGARCH)) { + // On LoongArch and RISC-V, relaxation might change relocations: copy from + // internal ones that are updated by relaxation. + InputSectionBase *sec = getRelocatedSection(); + copyRelocations(buf, llvm::make_range(sec->relocations.begin(), + sec->relocations.end())); + } else { + // Convert the raw relocations in the input section into Relocation objects + // suitable to be used by copyRelocations below. + struct MapRel { + const ObjFile &file; + Relocation operator()(const RelTy &rel) const { + // RelExpr is not used so set to a dummy value. + return Relocation{R_NONE, rel.getType(config->isMips64EL), rel.r_offset, + getAddend(rel), &file.getRelocTargetSym(rel)}; + } + }; + + using RawRels = ArrayRef; + using MapRelIter = + llvm::mapped_iterator; + auto mapRel = MapRel{*getFile()}; + RawRels rawRels = getDataAs(); + auto rels = llvm::make_range(MapRelIter(rawRels.begin(), mapRel), + MapRelIter(rawRels.end(), mapRel)); + copyRelocations(buf, rels); + } +} + // This is used for -r and --emit-relocs. We can't use memcpy to copy // relocations because we need to update symbol table offset and section index // for each relocation. So we copy relocations one by one. -template -void InputSection::copyRelocations(uint8_t *buf, ArrayRef rels) { +template +void InputSection::copyRelocations(uint8_t *buf, + llvm::iterator_range rels) { const TargetInfo &target = *elf::target; InputSectionBase *sec = getRelocatedSection(); (void)sec->contentMaybeDecompress(); // uncompress if needed - for (const RelTy &rel : rels) { - RelType type = rel.getType(config->isMips64EL); + for (const Relocation &rel : rels) { + RelType type = rel.type; const ObjFile *file = getFile(); - Symbol &sym = file->getRelocTargetSym(rel); + Symbol &sym = *rel.sym; auto *p = reinterpret_cast(buf); buf += sizeof(RelTy); if (RelTy::IsRela) - p->r_addend = getAddend(rel); + p->r_addend = rel.addend; // Output section VA is zero for -r, so r_offset is an offset within the // section, but for --emit-relocs it is a virtual address. - p->r_offset = sec->getVA(rel.r_offset); + p->r_offset = sec->getVA(rel.offset); p->setSymbolAndType(in.symTab->getSymbolIndex(&sym), type, config->isMips64EL); @@ -408,8 +441,8 @@ void InputSection::copyRelocations(uint8_t *buf, ArrayRef rels) { continue; } - int64_t addend = getAddend(rel); - const uint8_t *bufLoc = sec->content().begin() + rel.r_offset; + int64_t addend = rel.addend; + const uint8_t *bufLoc = sec->content().begin() + rel.offset; if (!RelTy::IsRela) addend = target.getImplicitAddend(bufLoc, type); @@ -432,7 +465,7 @@ void InputSection::copyRelocations(uint8_t *buf, ArrayRef rels) { if (RelTy::IsRela) p->r_addend = sym.getVA(addend) - section->getOutputSection()->addr; else if (config->relocatable && type != target.noneRel) - sec->addReloc({R_ABS, type, rel.r_offset, addend, &sym}); + sec->addReloc({R_ABS, type, rel.offset, addend, &sym}); } else if (config->emachine == EM_PPC && type == R_PPC_PLTREL24 && p->r_addend >= 0x8000 && sec->file->ppc32Got2) { // Similar to R_MIPS_GPREL{16,32}. If the addend of R_PPC_PLTREL24 @@ -1106,11 +1139,11 @@ template void InputSection::writeTo(uint8_t *buf) { // If -r or --emit-relocs is given, then an InputSection // may be a relocation section. if (LLVM_UNLIKELY(type == SHT_RELA)) { - copyRelocations(buf, getDataAs()); + copyRelocations(buf); return; } if (LLVM_UNLIKELY(type == SHT_REL)) { - copyRelocations(buf, getDataAs()); + copyRelocations(buf); return; } diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index 15122d6abd6b77f0a0767608ba8000d2c1ec9f16..842bc369909d22ff63e71d54744739d216d4d685 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -101,7 +101,23 @@ protected: link(link), info(info) {} }; -struct RISCVRelaxAux; +struct SymbolAnchor { + uint64_t offset; + Defined *d; + bool end; // true for the anchor of st_value+st_size +}; + +struct RelaxAux { + // This records symbol start and end offsets which will be adjusted according + // to the nearest relocDeltas element. + SmallVector anchors; + // For relocations[i], the actual offset is + // r_offset - (i ? relocDeltas[i-1] : 0). + std::unique_ptr relocDeltas; + // For relocations[i], the actual type is relocTypes[i]. + std::unique_ptr relocTypes; + SmallVector writes; +}; // This corresponds to a section of an input file. class InputSectionBase : public SectionBase { @@ -222,9 +238,9 @@ public: // basic blocks. JumpInstrMod *jumpInstrMod = nullptr; - // Auxiliary information for RISC-V linker relaxation. RISC-V does not use - // jumpInstrMod. - RISCVRelaxAux *relaxAux; + // Auxiliary information for RISC-V and LoongArch linker relaxation. + // They do not use jumpInstrMod. + RelaxAux *relaxAux; // The compressed content size when `compressed` is true. size_t compressedSize; @@ -396,8 +412,10 @@ public: static InputSection discarded; private: - template - void copyRelocations(uint8_t *buf, llvm::ArrayRef rels); + template void copyRelocations(uint8_t *buf); + + template + void copyRelocations(uint8_t *buf, llvm::iterator_range rels); template void copyShtGroup(uint8_t *buf); }; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index 47dbe6b4d1c65a48ac075d63d48219e1ed849f5e..bf831afa179305e4f2b8a5d84b333871b80857d6 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -94,6 +94,8 @@ public: // Do a linker relaxation pass and return true if we changed something. virtual bool relaxOnce(int pass) const { return false; } + // Do finalize relaxation after collecting relaxation infos. + virtual void finalizeRelax(int passes) const {} virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type, JumpModType val) const {} @@ -234,6 +236,7 @@ void addArmInputSectionMappingSymbols(); void addArmSyntheticSectionMappingSymbol(Defined *); void sortArmMappingSymbols(); void convertArmInstructionstoBE8(InputSection *sec, uint8_t *buf); +void initSymbolAnchors(); LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target; TargetInfo *getTarget(); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 368c9aabceae8111cc6f05015b1e56e1150281a5..dd37bbbf76c1840247e163626b643030c0d6e53d 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1668,8 +1668,8 @@ template void Writer::finalizeAddressDependentContent() { } } } - if (!config->relocatable && config->emachine == EM_RISCV) - riscvFinalizeRelax(pass); + if (!config->relocatable) + target->finalizeRelax(pass); if (config->relocatable) for (OutputSection *sec : outputSections) diff --git a/lld/test/ELF/loongarch-add-sub.s b/lld/test/ELF/loongarch-add-sub.s index 63a3f7de179e6b849aa9f5fac0b6d2b598e5ad71..35f8a053d69cef38c9d18ac1a0b827cf79ce2526 100644 --- a/lld/test/ELF/loongarch-add-sub.s +++ b/lld/test/ELF/loongarch-add-sub.s @@ -6,7 +6,7 @@ # RUN: llvm-readelf -x .rodata %t.la64 | FileCheck --check-prefix=CHECK %s # CHECK: section '.rodata': # CHECK-NEXT: 0x9876543210 10325476 98badcfe 804602be 79ffffff -# CHECK-NEXT: 0x9876543220 804602be 804680 +# CHECK-NEXT: 0x9876543220 804602be 80468097 .text .global _start @@ -34,3 +34,7 @@ quux: .byte 0 .reloc quux, R_LARCH_ADD8, 1b .reloc quux, R_LARCH_SUB8, 2b +qux: + .byte 0b10000000 + .reloc qux, R_LARCH_ADD6, qux + .reloc qux, R_LARCH_SUB6, 2b diff --git a/lld/test/ELF/loongarch-relax-align.s b/lld/test/ELF/loongarch-relax-align.s new file mode 100644 index 0000000000000000000000000000000000000000..ab61e15d5caca28ef83e42c2ff7efd444e8608fb --- /dev/null +++ b/lld/test/ELF/loongarch-relax-align.s @@ -0,0 +1,126 @@ +# REQUIRES: loongarch + +# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+relax %s -o %t.32.o +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o %t.64.o +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.text2=0x20000 -e 0 %t.32.o -o %t.32 +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.text2=0x20000 -e 0 %t.64.o -o %t.64 +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.text2=0x20000 -e 0 %t.32.o --no-relax -o %t.32n +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.text2=0x20000 -e 0 %t.64.o --no-relax -o %t.64n +# RUN: llvm-objdump -td --no-show-raw-insn %t.32 | FileCheck %s +# RUN: llvm-objdump -td --no-show-raw-insn %t.64 | FileCheck %s +# RUN: llvm-objdump -td --no-show-raw-insn %t.32n | FileCheck %s +# RUN: llvm-objdump -td --no-show-raw-insn %t.64n | FileCheck %s + +## Test the R_LARCH_ALIGN without symbol index. +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o %t.o64.o --defsym=old=1 +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.text2=0x20000 -e 0 %t.o64.o -o %t.o64 +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.text2=0x20000 -e 0 %t.o64.o --no-relax -o %t.o64n +# RUN: llvm-objdump -td --no-show-raw-insn %t.o64 | FileCheck %s +# RUN: llvm-objdump -td --no-show-raw-insn %t.o64n | FileCheck %s + +## -r keeps section contents unchanged. +# RUN: ld.lld -r %t.64.o -o %t.64.r +# RUN: llvm-objdump -dr --no-show-raw-insn %t.64.r | FileCheck %s --check-prefix=CHECKR + +# CHECK-DAG: {{0*}}10000 l .text {{0*}}44 .Ltext_start +# CHECK-DAG: {{0*}}10038 l .text {{0*}}0c .L1 +# CHECK-DAG: {{0*}}10040 l .text {{0*}}04 .L2 +# CHECK-DAG: {{0*}}20000 l .text2 {{0*}}14 .Ltext2_start + +# CHECK: <.Ltext_start>: +# CHECK-NEXT: break 1 +# CHECK-NEXT: break 2 +# CHECK-NEXT: nop +# CHECK-NEXT: nop +# CHECK-NEXT: break 3 +# CHECK-NEXT: break 4 +# CHECK-NEXT: nop +# CHECK-NEXT: nop +# CHECK-NEXT: pcalau12i $a0, 0 +# CHECK-NEXT: addi.{{[dw]}} $a0, $a0, 0 +# CHECK-NEXT: pcalau12i $a0, 0 +# CHECK-NEXT: addi.{{[dw]}} $a0, $a0, 56 +# CHECK-NEXT: pcalau12i $a0, 0 +# CHECK-NEXT: addi.{{[dw]}} $a0, $a0, 64 +# CHECK-EMPTY: +# CHECK-NEXT: <.L1>: +# CHECK-NEXT: nop +# CHECK-NEXT: nop +# CHECK-EMPTY: +# CHECK-NEXT: <.L2>: +# CHECK-NEXT: break 5 + +# CHECK: <.Ltext2_start>: +# CHECK-NEXT: pcalau12i $a0, 0 +# CHECK-NEXT: addi.{{[dw]}} $a0, $a0, 0 +# CHECK-NEXT: nop +# CHECK-NEXT: nop +# CHECK-NEXT: break 6 + +# CHECKR: <.Ltext2_start>: +# CHECKR-NEXT: pcalau12i $a0, 0 +# CHECKR-NEXT: {{0*}}00: R_LARCH_PCALA_HI20 .Ltext2_start +# CHECKR-NEXT: {{0*}}00: R_LARCH_RELAX *ABS* +# CHECKR-NEXT: addi.d $a0, $a0, 0 +# CHECKR-NEXT: {{0*}}04: R_LARCH_PCALA_LO12 .Ltext2_start +# CHECKR-NEXT: {{0*}}04: R_LARCH_RELAX *ABS* +# CHECKR-NEXT: nop +# CHECKR-NEXT: {{0*}}08: R_LARCH_ALIGN .Lalign_symbol+0x4 +# CHECKR-NEXT: nop +# CHECKR-NEXT: nop +# CHECKR-NEXT: break 6 + +.macro .fake_p2align_4 max=0 + .ifdef old + .if \max==0 + .reloc ., R_LARCH_ALIGN, 0xc + nop; nop; nop + .endif + .else + .reloc ., R_LARCH_ALIGN, .Lalign_symbol + 0x4 + (\max << 8) + nop; nop; nop + .endif +.endm + + .text +.Lalign_symbol: +.Ltext_start: + break 1 + break 2 +## +0x8: Emit 2 nops, delete 1 nop. + .fake_p2align_4 + + break 3 +## +0x14: Emit 3 nops > 8 bytes, not emit. + .fake_p2align_4 8 + + break 4 + .fake_p2align_4 8 +## +0x18: Emit 2 nops <= 8 bytes. + +## Compensate +.ifdef old + nop; nop +.endif + +## +0x20: Test symbol value and symbol size can be handled. + la.pcrel $a0, .Ltext_start + la.pcrel $a0, .L1 + la.pcrel $a0, .L2 + +## +0x38: Emit 2 nops, delete 1 nop. +.L1: + .fake_p2align_4 +.L2: + break 5 + .size .L1, . - .L1 + .size .L2, . - .L2 + .size .Ltext_start, . - .Ltext_start + +## Test another text section. + .section .text2,"ax",@progbits +.Ltext2_start: + la.pcrel $a0, .Ltext2_start + .fake_p2align_4 + break 6 + .size .Ltext2_start, . - .Ltext2_start diff --git a/lld/test/ELF/loongarch-relax-emit-relocs.s b/lld/test/ELF/loongarch-relax-emit-relocs.s new file mode 100644 index 0000000000000000000000000000000000000000..581fce8c95caa4e25a1d1d40459df7c1d34fb782 --- /dev/null +++ b/lld/test/ELF/loongarch-relax-emit-relocs.s @@ -0,0 +1,49 @@ +# REQUIRES: loongarch +## Test that we can handle --emit-relocs while relaxing. + +# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+relax %s -o %t.32.o +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o %t.64.o +# RUN: ld.lld -Ttext=0x10000 --emit-relocs %t.32.o -o %t.32 +# RUN: ld.lld -Ttext=0x10000 --emit-relocs %t.64.o -o %t.64 +# RUN: llvm-objdump -dr %t.32 | FileCheck %s +# RUN: llvm-objdump -dr %t.64 | FileCheck %s + +## -r should keep original relocations. +# RUN: ld.lld -r %t.64.o -o %t.64.r +# RUN: llvm-objdump -dr %t.64.r | FileCheck %s --check-prefix=CHECKR + +## --no-relax should keep original relocations. +## TODO Due to R_LARCH_RELAX is not relaxed, it plays same as --relax now. +# RUN: ld.lld -Ttext=0x10000 --emit-relocs --no-relax %t.64.o -o %t.64.norelax +# RUN: llvm-objdump -dr %t.64.norelax | FileCheck %s + +# CHECK: 00010000 <_start>: +# CHECK-NEXT: pcalau12i $a0, 0 +# CHECK-NEXT: R_LARCH_PCALA_HI20 _start +# CHECK-NEXT: R_LARCH_RELAX *ABS* +# CHECK-NEXT: addi.{{[dw]}} $a0, $a0, 0 +# CHECK-NEXT: R_LARCH_PCALA_LO12 _start +# CHECK-NEXT: R_LARCH_RELAX *ABS* +# CHECK-NEXT: nop +# CHECK-NEXT: R_LARCH_ALIGN .Lla-relax-align0+0x4 +# CHECK-NEXT: nop +# CHECK-NEXT: ret + +# CHECKR: <_start>: +# CHECKR-NEXT: pcalau12i $a0, 0 +# CHECKR-NEXT: R_LARCH_PCALA_HI20 _start +# CHECKR-NEXT: R_LARCH_RELAX *ABS* +# CHECKR-NEXT: addi.d $a0, $a0, 0 +# CHECKR-NEXT: R_LARCH_PCALA_LO12 _start +# CHECKR-NEXT: R_LARCH_RELAX *ABS* +# CHECKR-NEXT: nop +# CHECKR-NEXT: R_LARCH_ALIGN .Lla-relax-align0+0x4 +# CHECKR-NEXT: nop +# CHECKR-NEXT: nop +# CHECKR-NEXT: ret + +.global _start +_start: + la.pcrel $a0, _start + .p2align 4 + ret diff --git a/lld/test/ELF/loongarch-reloc-leb128.s b/lld/test/ELF/loongarch-reloc-leb128.s new file mode 100644 index 0000000000000000000000000000000000000000..7740ca797fca9910029d8b127964bff99824ecce --- /dev/null +++ b/lld/test/ELF/loongarch-reloc-leb128.s @@ -0,0 +1,102 @@ +# REQUIRES: loongarch +# RUN: rm -rf %t && split-file %s %t && cd %t + +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax a.s -o a.o +# RUN: llvm-readobj -r -x .gcc_except_table -x .debug_rnglists -x .debug_loclists a.o | FileCheck %s --check-prefix=REL +# RUN: ld.lld -shared --gc-sections a.o -o a.so +# RUN: llvm-readelf -x .gcc_except_table -x .debug_rnglists -x .debug_loclists a.so | FileCheck %s + +# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+relax a.s -o a32.o +# RUN: llvm-readobj -r -x .gcc_except_table -x .debug_rnglists -x .debug_loclists a32.o | FileCheck %s --check-prefix=REL +# RUN: ld.lld -shared --gc-sections a32.o -o a32.so +# RUN: llvm-readelf -x .gcc_except_table -x .debug_rnglists -x .debug_loclists a32.so | FileCheck %s + +# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+relax extraspace.s -o extraspace32.o +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax extraspace.s -o extraspace64.o +# RUN: not ld.lld -shared extraspace32.o 2>&1 | FileCheck %s --check-prefix=ERROR +# RUN: not ld.lld -shared extraspace64.o 2>&1 | FileCheck %s --check-prefix=ERROR +# ERROR: error: extraspace{{.*}}.o:(.rodata+0x0): extra space for uleb128 + +#--- a.s +.cfi_startproc +.cfi_lsda 0x1b,.LLSDA0 +.cfi_endproc + +.section .text.w,"axR" +break 0; break 0; break 0; w1: + .p2align 4 # 4 bytes after relaxation +w2: break 0 + +.section .text.x,"ax" +break 0; break 0; break 0; x1: + .p2align 4 # 4 bytes after relaxation +x2: break 0 + +.section .gcc_except_table,"a" +.LLSDA0: +.uleb128 w2-w1+116 # initial value: 0x0080 +.uleb128 w1-w2+141 # initial value: 0x0080 +.uleb128 w2-w1+16372 # initial value: 0x008080 +.uleb128 w1-w2+16397 # initial value: 0x008080 +.uleb128 w2-w1+2097140 # initial value: 0x00808080 +.uleb128 w1-w2+2097165 # initial value: 0x00808080 + +.section .debug_rnglists +.uleb128 w2-w1+116 # initial value: 0x0080 +.uleb128 w1-w2+141 # initial value: 0x0080 +.uleb128 w2-w1+16372 # initial value: 0x008080 +.uleb128 w1-w2+16397 # initial value: 0x008080 +.uleb128 w2-w1+2097140 # initial value: 0x00808080 +.uleb128 w1-w2+2097165 # initial value: 0x00808080 + +.section .debug_loclists +.uleb128 x2-x1 # references discarded symbols + +# REL: Section ({{.*}}) .rela.debug_rnglists { +# REL-NEXT: 0x0 R_LARCH_ADD_ULEB128 w2 0x74 +# REL-NEXT: 0x0 R_LARCH_SUB_ULEB128 w1 0x0 +# REL-NEXT: 0x2 R_LARCH_ADD_ULEB128 w1 0x8D +# REL-NEXT: 0x2 R_LARCH_SUB_ULEB128 w2 0x0 +# REL-NEXT: 0x4 R_LARCH_ADD_ULEB128 w2 0x3FF4 +# REL-NEXT: 0x4 R_LARCH_SUB_ULEB128 w1 0x0 +# REL-NEXT: 0x7 R_LARCH_ADD_ULEB128 w1 0x400D +# REL-NEXT: 0x7 R_LARCH_SUB_ULEB128 w2 0x0 +# REL-NEXT: 0xA R_LARCH_ADD_ULEB128 w2 0x1FFFF4 +# REL-NEXT: 0xA R_LARCH_SUB_ULEB128 w1 0x0 +# REL-NEXT: 0xE R_LARCH_ADD_ULEB128 w1 0x20000D +# REL-NEXT: 0xE R_LARCH_SUB_ULEB128 w2 0x0 +# REL-NEXT: } +# REL: Section ({{.*}}) .rela.debug_loclists { +# REL-NEXT: 0x0 R_LARCH_ADD_ULEB128 x2 0x0 +# REL-NEXT: 0x0 R_LARCH_SUB_ULEB128 x1 0x0 +# REL-NEXT: } + +# REL: Hex dump of section '.gcc_except_table': +# REL-NEXT: 0x00000000 80008000 80800080 80008080 80008080 . +# REL-NEXT: 0x00000010 8000 . +# REL: Hex dump of section '.debug_rnglists': +# REL-NEXT: 0x00000000 80008000 80800080 80008080 80008080 . +# REL-NEXT: 0x00000010 8000 . +# REL: Hex dump of section '.debug_loclists': +# REL-NEXT: 0x00000000 00 . + +# CHECK: Hex dump of section '.gcc_except_table': +# CHECK-NEXT: 0x[[#%x,]] f8008901 f8ff0089 8001f8ff ff008980 . +# CHECK-NEXT: 0x[[#%x,]] 8001 . +# CHECK: Hex dump of section '.debug_rnglists': +# CHECK-NEXT: 0x00000000 f8008901 f8ff0089 8001f8ff ff008980 . +# CHECK-NEXT: 0x00000010 8001 . +# CHECK: Hex dump of section '.debug_loclists': +# CHECK-NEXT: 0x00000000 0c . + +#--- extraspace.s +.text +w1: + la.pcrel $t0, w1 +w2: + +.rodata +.reloc ., R_LARCH_ADD_ULEB128, w2 +.reloc ., R_LARCH_SUB_ULEB128, w1 +.fill 10, 1, 0x80 +.byte 0 diff --git a/lld/test/ELF/riscv-relax-emit-relocs.s b/lld/test/ELF/riscv-relax-emit-relocs.s new file mode 100644 index 0000000000000000000000000000000000000000..ebd69b742d4f9dd07aacb994a00f991d2693e6cb --- /dev/null +++ b/lld/test/ELF/riscv-relax-emit-relocs.s @@ -0,0 +1,69 @@ +# REQUIRES: riscv +## Test that we can handle --emit-relocs while relaxing. + +# RUN: rm -rf %t && mkdir %t && cd %t + +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s -o 32.o +# RUN: ld.lld -Ttext=0x10000 --emit-relocs 32.o -o 32 +# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 32 | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o 64.o +# RUN: ld.lld -Ttext=0x10000 --emit-relocs 64.o -o 64 +# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64 | FileCheck %s + +## -r should keep original relocations. +# RUN: ld.lld -r 64.o -o 64.r +# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64.r | FileCheck %s --check-prefix=CHECKR + +## --no-relax should keep original relocations. +# RUN: ld.lld --emit-relocs --no-relax 64.o -o 64.norelax +# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64.norelax | FileCheck %s --check-prefix=CHECKNORELAX + +# CHECK: <_start>: +# CHECK-NEXT: jal ra, 0x10008 +# CHECK-NEXT: R_RISCV_JAL f +# CHECK-NEXT: R_RISCV_RELAX *ABS* +# CHECK-NEXT: jal ra, 0x10008 +# CHECK-NEXT: R_RISCV_JAL f +# CHECK-NEXT: R_RISCV_RELAX *ABS* +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: jalr zero, 0(ra) +# CHECK-NEXT: R_RISCV_ALIGN *ABS*+0x4 + +# CHECKR: <_start>: +# CHECKR-NEXT: auipc ra, 0 +# CHECKR-NEXT: R_RISCV_CALL_PLT f +# CHECKR-NEXT: R_RISCV_RELAX *ABS* +# CHECKR-NEXT: jalr ra, 0(ra) +# CHECKR-NEXT: auipc ra, 0 +# CHECKR-NEXT: R_RISCV_CALL_PLT f +# CHECKR-NEXT: R_RISCV_RELAX *ABS* +# CHECKR-NEXT: jalr ra, 0(ra) +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: R_RISCV_ALIGN *ABS*+0x4 +# CHECKR-EMPTY: +# CHECKR-NEXT: : +# CHECKR-NEXT: jalr zero, 0(ra) + +# CHECKNORELAX: <_start>: +# CHECKNORELAX-NEXT: auipc ra, 0 +# CHECKNORELAX-NEXT: R_RISCV_CALL_PLT f +# CHECKNORELAX-NEXT: R_RISCV_RELAX *ABS* +# CHECKNORELAX-NEXT: jalr ra, 16(ra) +# CHECKNORELAX-NEXT: auipc ra, 0 +# CHECKNORELAX-NEXT: R_RISCV_CALL_PLT f +# CHECKNORELAX-NEXT: R_RISCV_RELAX *ABS* +# CHECKNORELAX-NEXT: jalr ra, 8(ra) +# CHECKNORELAX-EMPTY: +# CHECKNORELAX-NEXT: : +# CHECKNORELAX-NEXT: jalr zero, 0(ra) +# CHECKNORELAX-NEXT: R_RISCV_ALIGN *ABS*+0x4 + +.global _start +_start: + call f + call f + .balign 8 +f: + ret diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def index 9a126df0153119534dc80e5be56f6b7a03d0fc07..c7fd6490041cd1daceaef94651ab3baf621879a5 100644 --- a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def @@ -55,3 +55,5 @@ ELF_RELOC(R_RISCV_SET32, 56) ELF_RELOC(R_RISCV_32_PCREL, 57) ELF_RELOC(R_RISCV_IRELATIVE, 58) ELF_RELOC(R_RISCV_PLT32, 59) +ELF_RELOC(R_RISCV_SET_ULEB128, 60) +ELF_RELOC(R_RISCV_SUB_ULEB128, 61) diff --git a/llvm/include/llvm/MC/MCAsmBackend.h b/llvm/include/llvm/MC/MCAsmBackend.h index 5e08fb41679ba9fabb8744f88f0a9a2b06db182f..968a767b17f80d1430c1661f9a9bf0fbfa9f1ce8 100644 --- a/llvm/include/llvm/MC/MCAsmBackend.h +++ b/llvm/include/llvm/MC/MCAsmBackend.h @@ -21,6 +21,7 @@ class MCAlignFragment; class MCDwarfCallFrameFragment; class MCDwarfLineAddrFragment; class MCFragment; +class MCLEBFragment; class MCRelaxableFragment; class MCSymbol; class MCAsmLayout; @@ -194,6 +195,13 @@ public: return false; } + // Defined by linker relaxation targets to possibly emit LEB128 relocations + // and set Value at the relocated location. + virtual std::pair + relaxLEB128(MCLEBFragment &LF, MCAsmLayout &Layout, int64_t &Value) const { + return std::make_pair(false, false); + } + /// @} /// Returns the minimum size of a nop in bytes on this target. The assembler diff --git a/llvm/include/llvm/MC/MCFixup.h b/llvm/include/llvm/MC/MCFixup.h index 069ca058310fccf26e54105b3207b559091e2f3c..7f48a90cb1ec44ad006430d5034b4beda093c874 100644 --- a/llvm/include/llvm/MC/MCFixup.h +++ b/llvm/include/llvm/MC/MCFixup.h @@ -25,6 +25,7 @@ enum MCFixupKind { FK_Data_4, ///< A four-byte fixup. FK_Data_8, ///< A eight-byte fixup. FK_Data_6b, ///< A six-bits fixup. + FK_Data_leb128, ///< A leb128 fixup. FK_PCRel_1, ///< A one-byte pc relative fixup. FK_PCRel_2, ///< A two-byte pc relative fixup. FK_PCRel_4, ///< A four-byte pc relative fixup. diff --git a/llvm/include/llvm/MC/MCFragment.h b/llvm/include/llvm/MC/MCFragment.h index 7be4792a45219838f5d1cc6eba7f02ca710d84f5..e965732010fe4c2c364b415d092e65fa027b6c2f 100644 --- a/llvm/include/llvm/MC/MCFragment.h +++ b/llvm/include/llvm/MC/MCFragment.h @@ -428,7 +428,7 @@ public: } }; -class MCLEBFragment : public MCFragment { +class MCLEBFragment final : public MCEncodedFragmentWithFixups<10, 1> { /// True if this is a sleb128, false if uleb128. bool IsSigned; @@ -439,17 +439,16 @@ class MCLEBFragment : public MCFragment { public: MCLEBFragment(const MCExpr &Value_, bool IsSigned_, MCSection *Sec = nullptr) - : MCFragment(FT_LEB, false, Sec), IsSigned(IsSigned_), Value(&Value_) { + : MCEncodedFragmentWithFixups<10, 1>(FT_LEB, false, Sec), + IsSigned(IsSigned_), Value(&Value_) { Contents.push_back(0); } const MCExpr &getValue() const { return *Value; } + void setValue(const MCExpr *Expr) { Value = Expr; } bool isSigned() const { return IsSigned; } - SmallString<8> &getContents() { return Contents; } - const SmallString<8> &getContents() const { return Contents; } - /// @} static bool classof(const MCFragment *F) { diff --git a/llvm/lib/MC/MCAsmBackend.cpp b/llvm/lib/MC/MCAsmBackend.cpp index 64bbc63719c7f74707d247e101a3a43184db0205..2eef7d363fe76a728eb9a9e823b3cba382e1527f 100644 --- a/llvm/lib/MC/MCAsmBackend.cpp +++ b/llvm/lib/MC/MCAsmBackend.cpp @@ -89,6 +89,7 @@ const MCFixupKindInfo &MCAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { {"FK_Data_4", 0, 32, 0}, {"FK_Data_8", 0, 64, 0}, {"FK_Data_6b", 0, 6, 0}, + {"FK_Data_leb128", 0, 0, 0}, {"FK_PCRel_1", 0, 8, MCFixupKindInfo::FKF_IsPCRel}, {"FK_PCRel_2", 0, 16, MCFixupKindInfo::FKF_IsPCRel}, {"FK_PCRel_4", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp index 55ed1a285cd735e6ac6514ee3027bbd0cb8ec67f..86c798ec9e27c6bdffab0d80cae13c83bac3417a 100644 --- a/llvm/lib/MC/MCAssembler.cpp +++ b/llvm/lib/MC/MCAssembler.cpp @@ -918,6 +918,12 @@ void MCAssembler::layout(MCAsmLayout &Layout) { Contents = DF.getContents(); break; } + case MCFragment::FT_LEB: { + auto &LF = cast(Frag); + Fixups = LF.getFixups(); + Contents = LF.getContents(); + break; + } case MCFragment::FT_PseudoProbe: { MCPseudoProbeAddrFragment &PF = cast(Frag); Fixups = PF.getFixups(); @@ -1006,12 +1012,31 @@ bool MCAssembler::relaxInstruction(MCAsmLayout &Layout, } bool MCAssembler::relaxLEB(MCAsmLayout &Layout, MCLEBFragment &LF) { - uint64_t OldSize = LF.getContents().size(); + const unsigned OldSize = static_cast(LF.getContents().size()); + unsigned PadTo = OldSize; int64_t Value; - bool Abs = LF.getValue().evaluateKnownAbsolute(Value, Layout); - if (!Abs) - report_fatal_error("sleb128 and uleb128 expressions must be absolute"); - SmallString<8> &Data = LF.getContents(); + SmallVectorImpl &Data = LF.getContents(); + LF.getFixups().clear(); + // Use evaluateKnownAbsolute for Mach-O as a hack: .subsections_via_symbols + // requires that .uleb128 A-B is foldable where A and B reside in different + // fragments. This is used by __gcc_except_table. + bool Abs = getSubsectionsViaSymbols() + ? LF.getValue().evaluateKnownAbsolute(Value, Layout) + : LF.getValue().evaluateAsAbsolute(Value, Layout); + if (!Abs) { + bool Relaxed, UseZeroPad; + std::tie(Relaxed, UseZeroPad) = getBackend().relaxLEB128(LF, Layout, Value); + if (!Relaxed) { + getContext().reportError(LF.getValue().getLoc(), + Twine(LF.isSigned() ? ".s" : ".u") + + "leb128 expression is not absolute"); + LF.setValue(MCConstantExpr::create(0, Context)); + } + uint8_t Tmp[10]; // maximum size: ceil(64/7) + PadTo = std::max(PadTo, encodeULEB128(uint64_t(Value), Tmp)); + if (UseZeroPad) + Value = 0; + } Data.clear(); raw_svector_ostream OSE(Data); // The compiler can generate EH table assembly that is impossible to assemble @@ -1019,9 +1044,9 @@ bool MCAssembler::relaxLEB(MCAsmLayout &Layout, MCLEBFragment &LF) { // to a later alignment fragment. To accommodate such tables, relaxation can // only increase an LEB fragment size here, not decrease it. See PR35809. if (LF.isSigned()) - encodeSLEB128(Value, OSE, OldSize); + encodeSLEB128(Value, OSE, PadTo); else - encodeULEB128(Value, OSE, OldSize); + encodeULEB128(Value, OSE, PadTo); return OldSize != LF.getContents().size(); } diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp index a7b980553af0553237f3bf5c0e0917b52315f84e..c9ff1865cf917e7582ee3eaf3643ba23aa6463b2 100644 --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -611,11 +611,6 @@ static void AttemptToFoldSymbolOffsetDifference( if (Asm->isThumbFunc(&SA)) Addend |= 1; - // If symbol is labeled as micromips, we set low-bit to ensure - // correct offset in .gcc_except_table - if (Asm->getBackend().isMicroMips(&SA)) - Addend |= 1; - // Clear the symbol expr pointers to indicate we have folded these // operands. A = B = nullptr; @@ -635,7 +630,8 @@ static void AttemptToFoldSymbolOffsetDifference( // instructions and InSet is false (not expressions in directive like // .size/.fill), disable the fast path. if (Layout && (InSet || !SecA.hasInstructions() || - !Asm->getContext().getTargetTriple().isRISCV())) { + !(Asm->getContext().getTargetTriple().isRISCV() || + Asm->getContext().getTargetTriple().isLoongArch()))) { // If both symbols are in the same fragment, return the difference of their // offsets. canGetFragmentOffset(FA) may be false. if (FA == FB && !SA.isVariable() && !SB.isVariable()) { @@ -706,8 +702,14 @@ static void AttemptToFoldSymbolOffsetDifference( } int64_t Num; + unsigned Count; if (DF) { Displacement += DF->getContents().size(); + } else if (auto *AF = dyn_cast(FI); + AF && Layout && AF->hasEmitNops() && + !Asm->getBackend().shouldInsertExtraNopBytesForCodeAlign( + *AF, Count)) { + Displacement += Asm->computeFragmentSize(*Layout, *AF); } else if (auto *FF = dyn_cast(FI); FF && FF->getNumValues().evaluateAsAbsolute(Num)) { Displacement += Num * FF->getValueSize(); diff --git a/llvm/lib/Object/RelocationResolver.cpp b/llvm/lib/Object/RelocationResolver.cpp index 03ac592895284b4c5f3f0cfe80b20c9139d706b0..0e5036d7dfcc531bfd974e63b83c26bb4f5c428d 100644 --- a/llvm/lib/Object/RelocationResolver.cpp +++ b/llvm/lib/Object/RelocationResolver.cpp @@ -539,6 +539,8 @@ static bool supportsLoongArch(uint64_t Type) { case ELF::R_LARCH_32: case ELF::R_LARCH_32_PCREL: case ELF::R_LARCH_64: + case ELF::R_LARCH_ADD6: + case ELF::R_LARCH_SUB6: case ELF::R_LARCH_ADD8: case ELF::R_LARCH_SUB8: case ELF::R_LARCH_ADD16: @@ -564,6 +566,10 @@ static uint64_t resolveLoongArch(uint64_t Type, uint64_t Offset, uint64_t S, return (S + Addend - Offset) & 0xFFFFFFFF; case ELF::R_LARCH_64: return S + Addend; + case ELF::R_LARCH_ADD6: + return (LocData & 0xC0) | ((LocData + S + Addend) & 0x3F); + case ELF::R_LARCH_SUB6: + return (LocData & 0xC0) | ((LocData - (S + Addend)) & 0x3F); case ELF::R_LARCH_ADD8: return (LocData + (S + Addend)) & 0xFF; case ELF::R_LARCH_SUB8: @@ -880,8 +886,10 @@ uint64_t resolveRelocation(RelocationResolver Resolver, const RelocationRef &R, if (GetRelSectionType() == ELF::SHT_RELA) { Addend = getELFAddend(R); - // RISCV relocations use both LocData and Addend. - if (Obj->getArch() != Triple::riscv32 && + // LoongArch and RISCV relocations use both LocData and Addend. + if (Obj->getArch() != Triple::loongarch32 && + Obj->getArch() != Triple::loongarch64 && + Obj->getArch() != Triple::riscv32 && Obj->getArch() != Triple::riscv64) LocData = 0; } diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp index 94d530306536fdd07b431ff862392572eec5675b..a132e645c8644f6a4cec2b8caff5fa4e801134df 100644 --- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp +++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp @@ -86,7 +86,7 @@ class LoongArchAsmParser : public MCTargetAsmParser { // "emitLoadAddress*" functions. void emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg, const MCExpr *Symbol, SmallVectorImpl &Insts, - SMLoc IDLoc, MCStreamer &Out); + SMLoc IDLoc, MCStreamer &Out, bool RelaxHint = false); // Helper to emit pseudo instruction "la.abs $rd, sym". void emitLoadAddressAbs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); @@ -749,12 +749,14 @@ bool LoongArchAsmParser::ParseInstruction(ParseInstructionInfo &Info, void LoongArchAsmParser::emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg, const MCExpr *Symbol, SmallVectorImpl &Insts, - SMLoc IDLoc, MCStreamer &Out) { + SMLoc IDLoc, MCStreamer &Out, + bool RelaxHint) { MCContext &Ctx = getContext(); for (LoongArchAsmParser::Inst &Inst : Insts) { unsigned Opc = Inst.Opc; LoongArchMCExpr::VariantKind VK = Inst.VK; - const LoongArchMCExpr *LE = LoongArchMCExpr::create(Symbol, VK, Ctx); + const LoongArchMCExpr *LE = + LoongArchMCExpr::create(Symbol, VK, Ctx, RelaxHint); switch (Opc) { default: llvm_unreachable("unexpected opcode"); @@ -855,7 +857,7 @@ void LoongArchAsmParser::emitLoadAddressPcrel(MCInst &Inst, SMLoc IDLoc, Insts.push_back( LoongArchAsmParser::Inst(ADDI, LoongArchMCExpr::VK_LoongArch_PCALA_LO12)); - emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); + emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out, true); } void LoongArchAsmParser::emitLoadAddressPcrelLarge(MCInst &Inst, SMLoc IDLoc, @@ -901,7 +903,7 @@ void LoongArchAsmParser::emitLoadAddressGot(MCInst &Inst, SMLoc IDLoc, Insts.push_back( LoongArchAsmParser::Inst(LD, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12)); - emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); + emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out, true); } void LoongArchAsmParser::emitLoadAddressGotLarge(MCInst &Inst, SMLoc IDLoc, diff --git a/llvm/lib/Target/LoongArch/LoongArch.td b/llvm/lib/Target/LoongArch/LoongArch.td index 0675caa3b60145dec04eb7d258ece4c27a74d9bc..75b65fe69f26291b79d848191928339727b554ae 100644 --- a/llvm/lib/Target/LoongArch/LoongArch.td +++ b/llvm/lib/Target/LoongArch/LoongArch.td @@ -102,6 +102,10 @@ def FeatureUAL : SubtargetFeature<"ual", "HasUAL", "true", "Allow memory accesses to be unaligned">; +def FeatureRelax + : SubtargetFeature<"relax", "HasLinkerRelax", "true", + "Enable Linker relaxation">; + //===----------------------------------------------------------------------===// // Registers, instruction descriptions ... //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/LoongArch/LoongArchSubtarget.h b/llvm/lib/Target/LoongArch/LoongArchSubtarget.h index 0fbe23f2f62d9d433ded91ba0aca4f38fc45ec4f..5c173675cca4ccb0a8a89b8cee07659af9f3eea5 100644 --- a/llvm/lib/Target/LoongArch/LoongArchSubtarget.h +++ b/llvm/lib/Target/LoongArch/LoongArchSubtarget.h @@ -43,6 +43,7 @@ class LoongArchSubtarget : public LoongArchGenSubtargetInfo { bool HasLaGlobalWithAbs = false; bool HasLaLocalWithAbs = false; bool HasUAL = false; + bool HasLinkerRelax = false; unsigned GRLen = 32; MVT GRLenVT = MVT::i32; LoongArchABI::ABI TargetABI = LoongArchABI::ABI_Unknown; @@ -100,6 +101,7 @@ public: bool hasLaGlobalWithAbs() const { return HasLaGlobalWithAbs; } bool hasLaLocalWithAbs() const { return HasLaLocalWithAbs; } bool hasUAL() const { return HasUAL; } + bool hasLinkerRelax() const { return HasLinkerRelax; } MVT getGRLenVT() const { return GRLenVT; } unsigned getGRLen() const { return GRLen; } LoongArchABI::ABI getTargetABI() const { return TargetABI; } diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp index ecb68ff401e9f40f9fd18afb6aa9a0f6e27f515d..8c482356402f44624d3e59e2657a7d56c81b442a 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp @@ -12,13 +12,18 @@ #include "LoongArchAsmBackend.h" #include "LoongArchFixupKinds.h" +#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCSection.h" #include "llvm/MC/MCValue.h" #include "llvm/Support/Endian.h" #include "llvm/Support/EndianStream.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/MathExtras.h" #define DEBUG_TYPE "loongarch-asmbackend" @@ -92,6 +97,7 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, case FK_Data_2: case FK_Data_4: case FK_Data_8: + case FK_Data_leb128: return Value; case LoongArch::fixup_loongarch_b16: { if (!isInt<18>(Value)) @@ -129,6 +135,15 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, } } +static void fixupLeb128(MCContext &Ctx, const MCFixup &Fixup, + MutableArrayRef Data, uint64_t Value) { + unsigned I; + for (I = 0; I != Data.size() && Value; ++I, Value >>= 7) + Data[I] |= uint8_t(Value & 0x7f); + if (Value) + Ctx.reportError(Fixup.getLoc(), "Invalid uleb128 value!"); +} + void LoongArchAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, @@ -144,6 +159,10 @@ void LoongArchAsmBackend::applyFixup(const MCAssembler &Asm, MCFixupKindInfo Info = getFixupKindInfo(Kind); MCContext &Ctx = Asm.getContext(); + // Fixup leb128 separately. + if (Fixup.getTargetKind() == FK_Data_leb128) + return fixupLeb128(Ctx, Fixup, Data, Value); + // Apply any target-specific value adjustments. Value = adjustFixupValue(Fixup, Value, Ctx); @@ -161,6 +180,70 @@ void LoongArchAsmBackend::applyFixup(const MCAssembler &Asm, } } +// Linker relaxation may change code size. We have to insert Nops +// for .align directive when linker relaxation enabled. So then Linker +// could satisfy alignment by removing Nops. +// The function returns the total Nops Size we need to insert. +bool LoongArchAsmBackend::shouldInsertExtraNopBytesForCodeAlign( + const MCAlignFragment &AF, unsigned &Size) { + // Calculate Nops Size only when linker relaxation enabled. + if (!AF.getSubtargetInfo()->hasFeature(LoongArch::FeatureRelax)) + return false; + + // Ignore alignment if MaxBytesToEmit is less than the minimum Nop size. + const unsigned MinNopLen = 4; + if (AF.getMaxBytesToEmit() < MinNopLen) + return false; + Size = AF.getAlignment().value() - MinNopLen; + return AF.getAlignment() > MinNopLen; +} + +// We need to insert R_LARCH_ALIGN relocation type to indicate the +// position of Nops and the total bytes of the Nops have been inserted +// when linker relaxation enabled. +// The function inserts fixup_loongarch_align fixup which eventually will +// transfer to R_LARCH_ALIGN relocation type. +// The improved R_LARCH_ALIGN requires symbol index. The lowest 8 bits of +// addend represent alignment and the other bits of addend represent the +// maximum number of bytes to emit. The maximum number of bytes is zero +// means ignore the emit limit. +bool LoongArchAsmBackend::shouldInsertFixupForCodeAlign( + MCAssembler &Asm, const MCAsmLayout &Layout, MCAlignFragment &AF) { + // Insert the fixup only when linker relaxation enabled. + if (!AF.getSubtargetInfo()->hasFeature(LoongArch::FeatureRelax)) + return false; + + // Calculate total Nops we need to insert. If there are none to insert + // then simply return. + unsigned Count; + if (!shouldInsertExtraNopBytesForCodeAlign(AF, Count)) + return false; + + MCSection *Sec = AF.getParent(); + MCContext &Ctx = Asm.getContext(); + const MCExpr *Dummy = MCConstantExpr::create(0, Ctx); + // Create fixup_loongarch_align fixup. + MCFixup Fixup = + MCFixup::create(0, Dummy, MCFixupKind(LoongArch::fixup_loongarch_align)); + const MCSymbolRefExpr *MCSym = getSecToAlignSym()[Sec]; + if (MCSym == nullptr) { + // Create a symbol and make the value of symbol is zero. + MCSymbol *Sym = Ctx.createNamedTempSymbol("la-relax-align"); + Sym->setFragment(&*Sec->getBeginSymbol()->getFragment()); + Asm.registerSymbol(*Sym); + MCSym = MCSymbolRefExpr::create(Sym, Ctx); + getSecToAlignSym()[Sec] = MCSym; + } + + uint64_t FixedValue = 0; + unsigned Lo = Log2_64(Count) + 1; + unsigned Hi = AF.getMaxBytesToEmit() >= Count ? 0 : AF.getMaxBytesToEmit(); + MCValue Value = MCValue::get(MCSym, nullptr, Hi << 8 | Lo); + Asm.getWriter().recordRelocation(Asm, Layout, &AF, Fixup, Value, FixedValue); + + return true; +} + bool LoongArchAsmBackend::shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target) { @@ -168,15 +251,186 @@ bool LoongArchAsmBackend::shouldForceRelocation(const MCAssembler &Asm, return true; switch (Fixup.getTargetKind()) { default: - return false; + return STI.hasFeature(LoongArch::FeatureRelax); case FK_Data_1: case FK_Data_2: case FK_Data_4: case FK_Data_8: + case FK_Data_leb128: return !Target.isAbsolute(); } } +static inline std::pair +getRelocPairForSize(unsigned Size) { + switch (Size) { + default: + llvm_unreachable("unsupported fixup size"); + case 6: + return std::make_pair( + MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_ADD6), + MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_SUB6)); + case 8: + return std::make_pair( + MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_ADD8), + MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_SUB8)); + case 16: + return std::make_pair( + MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_ADD16), + MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_SUB16)); + case 32: + return std::make_pair( + MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_ADD32), + MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_SUB32)); + case 64: + return std::make_pair( + MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_ADD64), + MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_SUB64)); + case 128: + return std::make_pair( + MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_ADD_ULEB128), + MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_SUB_ULEB128)); + } +} + +std::pair LoongArchAsmBackend::relaxLEB128(MCLEBFragment &LF, + MCAsmLayout &Layout, + int64_t &Value) const { + const MCExpr &Expr = LF.getValue(); + if (LF.isSigned() || !Expr.evaluateKnownAbsolute(Value, Layout)) + return std::make_pair(false, false); + LF.getFixups().push_back( + MCFixup::create(0, &Expr, FK_Data_leb128, Expr.getLoc())); + return std::make_pair(true, true); +} + +bool LoongArchAsmBackend::relaxDwarfLineAddr(MCDwarfLineAddrFragment &DF, + MCAsmLayout &Layout, + bool &WasRelaxed) const { + MCContext &C = Layout.getAssembler().getContext(); + + int64_t LineDelta = DF.getLineDelta(); + const MCExpr &AddrDelta = DF.getAddrDelta(); + SmallVectorImpl &Data = DF.getContents(); + SmallVectorImpl &Fixups = DF.getFixups(); + size_t OldSize = Data.size(); + + int64_t Value; + if (AddrDelta.evaluateAsAbsolute(Value, Layout)) + return false; + bool IsAbsolute = AddrDelta.evaluateKnownAbsolute(Value, Layout); + assert(IsAbsolute && "CFA with invalid expression"); + (void)IsAbsolute; + + Data.clear(); + Fixups.clear(); + raw_svector_ostream OS(Data); + + // INT64_MAX is a signal that this is actually a DW_LNE_end_sequence. + if (LineDelta != INT64_MAX) { + OS << uint8_t(dwarf::DW_LNS_advance_line); + encodeSLEB128(LineDelta, OS); + } + + unsigned Offset; + std::pair FK; + + // According to the DWARF specification, the `DW_LNS_fixed_advance_pc` opcode + // takes a single unsigned half (unencoded) operand. The maximum encodable + // value is therefore 65535. Set a conservative upper bound for relaxation. + if (Value > 60000) { + unsigned PtrSize = C.getAsmInfo()->getCodePointerSize(); + + OS << uint8_t(dwarf::DW_LNS_extended_op); + encodeULEB128(PtrSize + 1, OS); + + OS << uint8_t(dwarf::DW_LNE_set_address); + Offset = OS.tell(); + assert((PtrSize == 4 || PtrSize == 8) && "Unexpected pointer size"); + FK = getRelocPairForSize(PtrSize == 4 ? 32 : 64); + OS.write_zeros(PtrSize); + } else { + OS << uint8_t(dwarf::DW_LNS_fixed_advance_pc); + Offset = OS.tell(); + FK = getRelocPairForSize(16); + support::endian::write(OS, 0, support::little); + } + + const MCBinaryExpr &MBE = cast(AddrDelta); + Fixups.push_back(MCFixup::create(Offset, MBE.getLHS(), std::get<0>(FK))); + Fixups.push_back(MCFixup::create(Offset, MBE.getRHS(), std::get<1>(FK))); + + if (LineDelta == INT64_MAX) { + OS << uint8_t(dwarf::DW_LNS_extended_op); + OS << uint8_t(1); + OS << uint8_t(dwarf::DW_LNE_end_sequence); + } else { + OS << uint8_t(dwarf::DW_LNS_copy); + } + + WasRelaxed = OldSize != Data.size(); + return true; +} + +bool LoongArchAsmBackend::relaxDwarfCFA(MCDwarfCallFrameFragment &DF, + MCAsmLayout &Layout, + bool &WasRelaxed) const { + const MCExpr &AddrDelta = DF.getAddrDelta(); + SmallVectorImpl &Data = DF.getContents(); + SmallVectorImpl &Fixups = DF.getFixups(); + size_t OldSize = Data.size(); + + int64_t Value; + if (AddrDelta.evaluateAsAbsolute(Value, Layout)) + return false; + bool IsAbsolute = AddrDelta.evaluateKnownAbsolute(Value, Layout); + assert(IsAbsolute && "CFA with invalid expression"); + (void)IsAbsolute; + + Data.clear(); + Fixups.clear(); + raw_svector_ostream OS(Data); + + assert( + Layout.getAssembler().getContext().getAsmInfo()->getMinInstAlignment() == + 1 && + "expected 1-byte alignment"); + if (Value == 0) { + WasRelaxed = OldSize != Data.size(); + return true; + } + + auto AddFixups = [&Fixups, + &AddrDelta](unsigned Offset, + std::pair FK) { + const MCBinaryExpr &MBE = cast(AddrDelta); + Fixups.push_back(MCFixup::create(Offset, MBE.getLHS(), std::get<0>(FK))); + Fixups.push_back(MCFixup::create(Offset, MBE.getRHS(), std::get<1>(FK))); + }; + + if (isUIntN(6, Value)) { + OS << uint8_t(dwarf::DW_CFA_advance_loc); + AddFixups(0, getRelocPairForSize(6)); + } else if (isUInt<8>(Value)) { + OS << uint8_t(dwarf::DW_CFA_advance_loc1); + support::endian::write(OS, 0, support::little); + AddFixups(1, getRelocPairForSize(8)); + } else if (isUInt<16>(Value)) { + OS << uint8_t(dwarf::DW_CFA_advance_loc2); + support::endian::write(OS, 0, support::little); + AddFixups(1, getRelocPairForSize(16)); + } else if (isUInt<32>(Value)) { + OS << uint8_t(dwarf::DW_CFA_advance_loc4); + support::endian::write(OS, 0, support::little); + AddFixups(1, getRelocPairForSize(32)); + } else { + llvm_unreachable("unsupported CFA encoding"); + } + + WasRelaxed = OldSize != Data.size(); + return true; +} + bool LoongArchAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, const MCSubtargetInfo *STI) const { // We mostly follow binutils' convention here: align to 4-byte boundary with a @@ -191,9 +445,69 @@ bool LoongArchAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, return true; } +bool LoongArchAsmBackend::handleAddSubRelocations(const MCAsmLayout &Layout, + const MCFragment &F, + const MCFixup &Fixup, + const MCValue &Target, + uint64_t &FixedValue) const { + std::pair FK; + uint64_t FixedValueA, FixedValueB; + const MCSymbol &SA = Target.getSymA()->getSymbol(); + const MCSymbol &SB = Target.getSymB()->getSymbol(); + + bool force = !SA.isInSection() || !SB.isInSection(); + if (!force) { + const MCSection &SecA = SA.getSection(); + const MCSection &SecB = SB.getSection(); + + // We need record relocation if SecA != SecB. Usually SecB is same as the + // section of Fixup, which will be record the relocation as PCRel. If SecB + // is not same as the section of Fixup, it will report error. Just return + // false and then this work can be finished by handleFixup. + if (&SecA != &SecB) + return false; + + // In SecA == SecB case. If the linker relaxation is enabled, we need record + // the ADD, SUB relocations. Otherwise the FixedValue has already been calc- + // ulated out in evaluateFixup, return true and avoid record relocations. + if (!STI.hasFeature(LoongArch::FeatureRelax)) + return true; + } + + switch (Fixup.getKind()) { + case llvm::FK_Data_1: + FK = getRelocPairForSize(8); + break; + case llvm::FK_Data_2: + FK = getRelocPairForSize(16); + break; + case llvm::FK_Data_4: + FK = getRelocPairForSize(32); + break; + case llvm::FK_Data_8: + FK = getRelocPairForSize(64); + break; + case llvm::FK_Data_leb128: + FK = getRelocPairForSize(128); + break; + default: + llvm_unreachable("unsupported fixup size"); + } + MCValue A = MCValue::get(Target.getSymA(), nullptr, Target.getConstant()); + MCValue B = MCValue::get(Target.getSymB()); + auto FA = MCFixup::create(Fixup.getOffset(), nullptr, std::get<0>(FK)); + auto FB = MCFixup::create(Fixup.getOffset(), nullptr, std::get<1>(FK)); + auto &Asm = Layout.getAssembler(); + Asm.getWriter().recordRelocation(Asm, Layout, &F, FA, A, FixedValueA); + Asm.getWriter().recordRelocation(Asm, Layout, &F, FB, B, FixedValueB); + FixedValue = FixedValueA - FixedValueB; + return true; +} + std::unique_ptr LoongArchAsmBackend::createObjectTargetWriter() const { - return createLoongArchELFObjectWriter(OSABI, Is64Bit); + return createLoongArchELFObjectWriter( + OSABI, Is64Bit, STI.hasFeature(LoongArch::FeatureRelax)); } MCAsmBackend *llvm::createLoongArchAsmBackend(const Target &T, diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h index ae9bb8af041983e0bd8f894c62561249eebdb8d8..71bbd003888a58ab19183f3fa6fd53460a2f7c8e 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h @@ -17,7 +17,9 @@ #include "MCTargetDesc/LoongArchFixupKinds.h" #include "MCTargetDesc/LoongArchMCTargetDesc.h" #include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCExpr.h" #include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCSection.h" #include "llvm/MC/MCSubtargetInfo.h" namespace llvm { @@ -27,19 +29,34 @@ class LoongArchAsmBackend : public MCAsmBackend { uint8_t OSABI; bool Is64Bit; const MCTargetOptions &TargetOptions; + DenseMap SecToAlignSym; public: LoongArchAsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI, bool Is64Bit, const MCTargetOptions &Options) - : MCAsmBackend(support::little), STI(STI), OSABI(OSABI), Is64Bit(Is64Bit), - TargetOptions(Options) {} + : MCAsmBackend(support::little, + LoongArch::fixup_loongarch_relax), + STI(STI), OSABI(OSABI), Is64Bit(Is64Bit), TargetOptions(Options) {} ~LoongArchAsmBackend() override {} + bool handleAddSubRelocations(const MCAsmLayout &Layout, const MCFragment &F, + const MCFixup &Fixup, const MCValue &Target, + uint64_t &FixedValue) const override; + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef Data, uint64_t Value, bool IsResolved, const MCSubtargetInfo *STI) const override; + // Return Size with extra Nop Bytes for alignment directive in code section. + bool shouldInsertExtraNopBytesForCodeAlign(const MCAlignFragment &AF, + unsigned &Size) override; + + // Insert target specific fixup type for alignment directive in code section. + bool shouldInsertFixupForCodeAlign(MCAssembler &Asm, + const MCAsmLayout &Layout, + MCAlignFragment &AF) override; + bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target) override; @@ -60,12 +77,23 @@ public: void relaxInstruction(MCInst &Inst, const MCSubtargetInfo &STI) const override {} + std::pair relaxLEB128(MCLEBFragment &LF, MCAsmLayout &Layout, + int64_t &Value) const override; + + bool relaxDwarfLineAddr(MCDwarfLineAddrFragment &DF, MCAsmLayout &Layout, + bool &WasRelaxed) const override; + bool relaxDwarfCFA(MCDwarfCallFrameFragment &DF, MCAsmLayout &Layout, + bool &WasRelaxed) const override; + bool writeNopData(raw_ostream &OS, uint64_t Count, const MCSubtargetInfo *STI) const override; std::unique_ptr createObjectTargetWriter() const override; const MCTargetOptions &getTargetOptions() const { return TargetOptions; } + DenseMap &getSecToAlignSym() { + return SecToAlignSym; + } }; } // end namespace llvm diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFObjectWriter.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFObjectWriter.cpp index a6b9c0652639fbcc7c95e7ab70ff8990957e2f9d..e60b9c2cfd97cc02dfbf71a7db7e7bdc11d5c250 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFObjectWriter.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFObjectWriter.cpp @@ -20,19 +20,27 @@ using namespace llvm; namespace { class LoongArchELFObjectWriter : public MCELFObjectTargetWriter { public: - LoongArchELFObjectWriter(uint8_t OSABI, bool Is64Bit); + LoongArchELFObjectWriter(uint8_t OSABI, bool Is64Bit, bool EnableRelax); ~LoongArchELFObjectWriter() override; + bool needsRelocateWithSymbol(const MCSymbol &Sym, + unsigned Type) const override { + return EnableRelax; + } + protected: unsigned getRelocType(MCContext &Ctx, const MCValue &Target, const MCFixup &Fixup, bool IsPCRel) const override; + bool EnableRelax; }; } // end namespace -LoongArchELFObjectWriter::LoongArchELFObjectWriter(uint8_t OSABI, bool Is64Bit) +LoongArchELFObjectWriter::LoongArchELFObjectWriter(uint8_t OSABI, bool Is64Bit, + bool EnableRelax) : MCELFObjectTargetWriter(Is64Bit, OSABI, ELF::EM_LOONGARCH, - /*HasRelocationAddend*/ true) {} + /*HasRelocationAddend=*/true), + EnableRelax(EnableRelax) {} LoongArchELFObjectWriter::~LoongArchELFObjectWriter() {} @@ -87,6 +95,6 @@ unsigned LoongArchELFObjectWriter::getRelocType(MCContext &Ctx, } std::unique_ptr -llvm::createLoongArchELFObjectWriter(uint8_t OSABI, bool Is64Bit) { - return std::make_unique(OSABI, Is64Bit); +llvm::createLoongArchELFObjectWriter(uint8_t OSABI, bool Is64Bit, bool Relax) { + return std::make_unique(OSABI, Is64Bit, Relax); } diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h index ba2d6718cdf9a27ec3bf587a495fbf7009c792c8..78414408f21f070a1205ad2645f4a20e32f1132d 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h @@ -106,7 +106,11 @@ enum Fixups { // 20-bit fixup corresponding to %gd_pc_hi20(foo) for instruction pcalau12i. fixup_loongarch_tls_gd_pc_hi20, // 20-bit fixup corresponding to %gd_hi20(foo) for instruction lu12i.w. - fixup_loongarch_tls_gd_hi20 + fixup_loongarch_tls_gd_hi20, + // Generate an R_LARCH_RELAX which indicates the linker may relax here. + fixup_loongarch_relax = FirstLiteralRelocationKind + ELF::R_LARCH_RELAX, + // Generate an R_LARCH_ALIGN which indicates the linker may fixup align here. + fixup_loongarch_align = FirstLiteralRelocationKind + ELF::R_LARCH_ALIGN, }; } // end namespace LoongArch } // end namespace llvm diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp index 03fb9e008ae99ac68227cfc95e8fdd00bb7ca305..08c0820cb86291200039506e77573c615f0a9db7 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp @@ -19,6 +19,7 @@ #include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/Casting.h" #include "llvm/Support/EndianStream.h" @@ -120,12 +121,15 @@ LoongArchMCCodeEmitter::getExprOpValue(const MCInst &MI, const MCOperand &MO, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { assert(MO.isExpr() && "getExprOpValue expects only expressions"); + bool RelaxCandidate = false; + bool EnableRelax = STI.hasFeature(LoongArch::FeatureRelax); const MCExpr *Expr = MO.getExpr(); MCExpr::ExprKind Kind = Expr->getKind(); LoongArch::Fixups FixupKind = LoongArch::fixup_loongarch_invalid; if (Kind == MCExpr::Target) { const LoongArchMCExpr *LAExpr = cast(Expr); + RelaxCandidate = LAExpr->getRelaxHint(); switch (LAExpr->getKind()) { case LoongArchMCExpr::VK_LoongArch_None: case LoongArchMCExpr::VK_LoongArch_Invalid: @@ -269,6 +273,15 @@ LoongArchMCCodeEmitter::getExprOpValue(const MCInst &MI, const MCOperand &MO, Fixups.push_back( MCFixup::create(0, Expr, MCFixupKind(FixupKind), MI.getLoc())); + + // Emit an R_LARCH_RELAX if linker relaxation is enabled and LAExpr has relax + // hint. + if (EnableRelax && RelaxCandidate) { + const MCConstantExpr *Dummy = MCConstantExpr::create(0, Ctx); + Fixups.push_back(MCFixup::create( + 0, Dummy, MCFixupKind(LoongArch::fixup_loongarch_relax), MI.getLoc())); + } + return 0; } diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.cpp index 993111552a31430cd3111efae969680e6bb7e4aa..82c992b1cc8c4e899d4c390eb700b63ad591eb26 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.cpp @@ -25,9 +25,10 @@ using namespace llvm; #define DEBUG_TYPE "loongarch-mcexpr" -const LoongArchMCExpr * -LoongArchMCExpr::create(const MCExpr *Expr, VariantKind Kind, MCContext &Ctx) { - return new (Ctx) LoongArchMCExpr(Expr, Kind); +const LoongArchMCExpr *LoongArchMCExpr::create(const MCExpr *Expr, + VariantKind Kind, MCContext &Ctx, + bool Hint) { + return new (Ctx) LoongArchMCExpr(Expr, Kind, Hint); } void LoongArchMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.h index 0945cf82db865cdaa3c2edc412d0eb3c96e8ba39..93251f8241033b60e6fcdd42dd0dc27a2f757772 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.h @@ -67,16 +67,18 @@ public: private: const MCExpr *Expr; const VariantKind Kind; + const bool RelaxHint; - explicit LoongArchMCExpr(const MCExpr *Expr, VariantKind Kind) - : Expr(Expr), Kind(Kind) {} + explicit LoongArchMCExpr(const MCExpr *Expr, VariantKind Kind, bool Hint) + : Expr(Expr), Kind(Kind), RelaxHint(Hint) {} public: static const LoongArchMCExpr *create(const MCExpr *Expr, VariantKind Kind, - MCContext &Ctx); + MCContext &Ctx, bool Hint = false); VariantKind getKind() const { return Kind; } const MCExpr *getSubExpr() const { return Expr; } + bool getRelaxHint() const { return RelaxHint; } void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCTargetDesc.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCTargetDesc.h index ab35a0096c8a2a2f74cd209d0d072ef55063504b..bb05baa9b717c2c929ba73b6cd89a9db8b2c5bb9 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCTargetDesc.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCTargetDesc.h @@ -36,7 +36,7 @@ MCAsmBackend *createLoongArchAsmBackend(const Target &T, const MCTargetOptions &Options); std::unique_ptr -createLoongArchELFObjectWriter(uint8_t OSABI, bool Is64Bit); +createLoongArchELFObjectWriter(uint8_t OSABI, bool Is64Bit, bool Relax); } // end namespace llvm diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp index 1b890fbe041a7e2a2776cbad42f79840ccda5618..5c651aa9322515807cecd73ea83eb3de50c9a030 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -19,6 +19,7 @@ #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCValue.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Endian.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/ErrorHandling.h" @@ -27,6 +28,13 @@ using namespace llvm; +// Temporary workaround for old linkers that do not support ULEB128 relocations, +// which are abused by DWARF v5 DW_LLE_offset_pair/DW_RLE_offset_pair +// implemented in Clang/LLVM. +static cl::opt ULEB128Reloc( + "riscv-uleb128-reloc", cl::init(false), cl::Hidden, + cl::desc("Emit R_RISCV_SET_ULEB128/E_RISCV_SUB_ULEB128 if appropriate")); + std::optional RISCVAsmBackend::getFixupKind(StringRef Name) const { if (STI.getTargetTriple().isOSBinFormatELF()) { unsigned Type; @@ -126,6 +134,7 @@ bool RISCVAsmBackend::shouldForceRelocation(const MCAssembler &Asm, case FK_Data_2: case FK_Data_4: case FK_Data_8: + case FK_Data_leb128: if (Target.isAbsolute()) return false; break; @@ -330,6 +339,19 @@ bool RISCVAsmBackend::relaxDwarfCFA(MCDwarfCallFrameFragment &DF, return true; } +std::pair RISCVAsmBackend::relaxLEB128(MCLEBFragment &LF, + MCAsmLayout &Layout, + int64_t &Value) const { + if (LF.isSigned()) + return std::make_pair(false, false); + const MCExpr &Expr = LF.getValue(); + if (ULEB128Reloc) { + LF.getFixups().push_back( + MCFixup::create(0, &Expr, FK_Data_leb128, Expr.getLoc())); + } + return std::make_pair(Expr.evaluateKnownAbsolute(Value, Layout), false); +} + // Given a compressed control flow instruction this function returns // the expanded instruction. unsigned RISCVAsmBackend::getRelaxedOpcode(unsigned Op) const { @@ -416,6 +438,7 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, case FK_Data_4: case FK_Data_8: case FK_Data_6b: + case FK_Data_leb128: return Value; case RISCV::fixup_riscv_set_6b: return Value & 0x03; @@ -596,6 +619,10 @@ bool RISCVAsmBackend::handleAddSubRelocations(const MCAsmLayout &Layout, TA = ELF::R_RISCV_ADD64; TB = ELF::R_RISCV_SUB64; break; + case llvm::FK_Data_leb128: + TA = ELF::R_RISCV_SET_ULEB128; + TB = ELF::R_RISCV_SUB_ULEB128; + break; default: llvm_unreachable("unsupported fixup size"); } diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h index 0ea1f32e82963152a8163e7133c11d7b5524ef8f..edefb171bcdcb9841313b9b7ca2919734a2aec03 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h @@ -99,6 +99,8 @@ public: bool &WasRelaxed) const override; bool relaxDwarfCFA(MCDwarfCallFrameFragment &DF, MCAsmLayout &Layout, bool &WasRelaxed) const override; + std::pair relaxLEB128(MCLEBFragment &LF, MCAsmLayout &Layout, + int64_t &Value) const override; bool writeNopData(raw_ostream &OS, uint64_t Count, const MCSubtargetInfo *STI) const override; diff --git a/llvm/test/CodeGen/Mips/micromips-b-range.ll b/llvm/test/CodeGen/Mips/micromips-b-range.ll index 064afff3da0eb089fde4d9412485884139f75d26..81d1c04208ccf2a7e152d5d005ea35e79ac8e697 100644 --- a/llvm/test/CodeGen/Mips/micromips-b-range.ll +++ b/llvm/test/CodeGen/Mips/micromips-b-range.ll @@ -13,7 +13,7 @@ ; CHECK-NEXT: 1e: fb fd 00 00 sw $ra, 0($sp) ; CHECK-NEXT: 22: 41 a1 00 01 lui $1, 1 ; CHECK-NEXT: 26: 40 60 00 02 bal 0x2e -; CHECK-NEXT: 2a: 30 21 04 69 addiu $1, $1, 1129 +; CHECK-NEXT: 2a: 30 21 04 68 addiu $1, $1, 1128 ; CHECK-NEXT: 2e: 00 3f 09 50 addu $1, $ra, $1 ; CHECK-NEXT: 32: ff fd 00 00 lw $ra, 0($sp) ; CHECK-NEXT: 36: 00 01 0f 3c jr $1 @@ -27,7 +27,7 @@ ; CHECK-NEXT: 56: fb fd 00 00 sw $ra, 0($sp) ; CHECK-NEXT: 5a: 41 a1 00 01 lui $1, 1 ; CHECK-NEXT: 5e: 40 60 00 02 bal 0x66 -; CHECK-NEXT: 62: 30 21 04 5d addiu $1, $1, 1117 +; CHECK-NEXT: 62: 30 21 04 5c addiu $1, $1, 1116 ; CHECK-NEXT: 66: 00 3f 09 50 addu $1, $ra, $1 ; CHECK-NEXT: 6a: ff fd 00 00 lw $ra, 0($sp) ; CHECK-NEXT: 6e: 00 01 0f 3c jr $1 @@ -39,7 +39,7 @@ ; CHECK-NEXT: 86: fb fd 00 00 sw $ra, 0($sp) ; CHECK-NEXT: 8a: 41 a1 00 01 lui $1, 1 ; CHECK-NEXT: 8e: 40 60 00 02 bal 0x96 -; CHECK-NEXT: 92: 30 21 04 2d addiu $1, $1, 1069 +; CHECK-NEXT: 92: 30 21 04 2c addiu $1, $1, 1068 ; CHECK-NEXT: 96: 00 3f 09 50 addu $1, $ra, $1 ; CHECK-NEXT: 9a: ff fd 00 00 lw $ra, 0($sp) ; CHECK-NEXT: 9e: 00 01 0f 3c jr $1 @@ -51,7 +51,7 @@ ; CHECK-NEXT: 10476: fb fd 00 00 sw $ra, 0($sp) ; CHECK-NEXT: 1047a: 41 a1 00 01 lui $1, 1 ; CHECK-NEXT: 1047e: 40 60 00 02 bal 0x10486 -; CHECK-NEXT: 10482: 30 21 04 01 addiu $1, $1, 1025 +; CHECK-NEXT: 10482: 30 21 04 00 addiu $1, $1, 1024 ; CHECK-NEXT: 10486: 00 3f 09 50 addu $1, $ra, $1 ; CHECK-NEXT: 1048a: ff fd 00 00 lw $ra, 0($sp) ; CHECK-NEXT: 1048e: 00 01 0f 3c jr $1 diff --git a/llvm/test/CodeGen/Mips/micromips-gcc-except-table.ll b/llvm/test/CodeGen/Mips/micromips-gcc-except-table.ll index 2b63aff01574eda7eef9e80b0313eb12cb5755bb..20d64fc216b79b6e9a4ddb1683ea4cdac5ebd57d 100644 --- a/llvm/test/CodeGen/Mips/micromips-gcc-except-table.ll +++ b/llvm/test/CodeGen/Mips/micromips-gcc-except-table.ll @@ -1,7 +1,7 @@ ; RUN: llc -mtriple=mips-linux-gnu -mcpu=mips32r2 -mattr=+micromips -O3 -filetype=obj < %s | llvm-objdump -s -j .gcc_except_table - | FileCheck %s ; CHECK: Contents of section .gcc_except_table: -; CHECK-NEXT: 0000 ff9b1501 0c011100 00110e1f 011f1800 +; CHECK-NEXT: 0000 ff9b1501 0c001000 00100e1e 011e1800 ; CHECK-NEXT: 0010 00010000 00000000 @_ZTIi = external constant ptr diff --git a/llvm/test/DebugInfo/LoongArch/dwarf-loongarch-relocs.ll b/llvm/test/DebugInfo/LoongArch/dwarf-loongarch-relocs.ll new file mode 100644 index 0000000000000000000000000000000000000000..07443a62b93391b74d0650ea47ebd4316657f41e --- /dev/null +++ b/llvm/test/DebugInfo/LoongArch/dwarf-loongarch-relocs.ll @@ -0,0 +1,137 @@ +; RUN: llc --filetype=obj --mtriple=loongarch64 --mattr=-relax %s -o %t.o +; RUN: llvm-readobj -r %t.o | FileCheck --check-prefixes=RELOCS-BOTH,RELOCS-NORL %s +; RUN: llvm-objdump --source %t.o | FileCheck --check-prefixes=SOURCE,SOURCE-NORL %s +; RUN: llvm-dwarfdump --debug-info --debug-line %t.o | FileCheck --check-prefixes=DWARF,DWARF-NORL %s + +; RUN: llc --filetype=obj --mtriple=loongarch64 --mattr=+relax %s -o %t.r.o +; RUN: llvm-readobj -r %t.r.o | FileCheck --check-prefixes=RELOCS-BOTH,RELOCS-ENRL %s +; RUN: llvm-objdump --source %t.r.o | FileCheck --check-prefixes=SOURCE,SOURCE-ENRL %s +; RUN: llvm-dwarfdump --debug-info --debug-line %t.r.o | FileCheck --check-prefixes=DWARF,DWARF-ENRL %s + +; RELOCS-BOTH: Relocations [ +; RELOCS-BOTH-NEXT: Section ({{.*}}) .rela.text { +; RELOCS-NORL-NEXT: 0x14 R_LARCH_PCALA_HI20 sym 0x0 +; RELOCS-NORL-NEXT: 0x18 R_LARCH_PCALA_LO12 sym 0x0 +; RELOCS-ENRL-NEXT: 0x0 R_LARCH_ALIGN .Lla-relax-align0 0x5 +; RELOCS-ENRL-NEXT: 0x30 R_LARCH_PCALA_HI20 sym 0x0 +; RELOCS-ENRL-NEXT: 0x30 R_LARCH_RELAX - 0x0 +; RELOCS-ENRL-NEXT: 0x34 R_LARCH_PCALA_LO12 sym 0x0 +; RELOCS-ENRL-NEXT: 0x34 R_LARCH_RELAX - 0x0 +; RELOCS-BOTH-NEXT: } +; RELOCS-BOTH: Section ({{.*}}) .rela.debug_frame { +; RELOCS-NORL-NEXT: 0x1C R_LARCH_32 .debug_frame 0x0 +; RELOCS-NORL-NEXT: 0x20 R_LARCH_64 .text 0x0 +; RELOCS-ENRL-NEXT: 0x1C R_LARCH_32 0x0 +; RELOCS-ENRL-NEXT: 0x20 R_LARCH_64 0x0 +; RELOCS-ENRL-NEXT: 0x28 R_LARCH_ADD64 0x0 +; RELOCS-ENRL-NEXT: 0x28 R_LARCH_SUB64 0x0 +; RELOCS-ENRL-NEXT: 0x3F R_LARCH_ADD6 0x0 +; RELOCS-ENRL-NEXT: 0x3F R_LARCH_SUB6 0x0 +; RELOCS-BOTH-NEXT: } +; RELOCS-BOTH: Section ({{.*}}) .rela.debug_line { +; RELOCS-BOTH-NEXT: 0x22 R_LARCH_32 .debug_line_str 0x0 +; RELOCS-BOTH-NEXT: 0x31 R_LARCH_32 .debug_line_str 0x2 +; RELOCS-BOTH-NEXT: 0x46 R_LARCH_32 .debug_line_str 0x1B +; RELOCS-NORL-NEXT: 0x4F R_LARCH_64 .text 0x0 +; RELOCS-ENRL-NEXT: 0x4F R_LARCH_64 0x0 +; RELOCS-ENRL-NEXT: 0x5F R_LARCH_ADD16 0x0 +; RELOCS-ENRL-NEXT: 0x5F R_LARCH_SUB16 0x0 +; RELOCS-BOTH-NEXT: } +; RELOCS-BOTH-NEXT: ] + +; SOURCE-NORL: 0000000000000000 : +; SOURCE-ENRL: 000000000000001c : +; SOURCE: ; { +; SOURCE: ; asm volatile( +; SOURCE: ; return 0; + +; DWARF: DW_AT_producer ("clang") +; DWARF: DW_AT_name ("dwarf-loongarch-relocs.c") +; DWARF: DW_AT_comp_dir (".") +; DWARF: DW_AT_name ("foo") +; DWARF-NEXT: DW_AT_decl_file ("{{.*}}dwarf-loongarch-relocs.c") +; DWARF-NEXT: DW_AT_decl_line (1) +; DWARF-NEXT: DW_AT_type (0x00000032 "int") +; DWARF: DW_AT_name ("int") +; DWARF-NEXT: DW_AT_encoding (DW_ATE_signed) +; DWARF-NEXT: DW_AT_byte_size (0x04) +; DWARF: .debug_line contents: +; DWARF-NEXT: debug_line[0x00000000] +; DWARF-NEXT: Line table prologue: +; DWARF-NEXT: total_length: {{.*}} +; DWARF-NEXT: format: DWARF32 +; DWARF-NEXT: version: 5 +; DWARF-NEXT: address_size: 8 +; DWARF-NEXT: seg_select_size: 0 +; DWARF-NEXT: prologue_length: 0x0000003e +; DWARF-NEXT: min_inst_length: 1 +; DWARF-NEXT: max_ops_per_inst: 1 +; DWARF-NEXT: default_is_stmt: 1 +; DWARF-NEXT: line_base: -5 +; DWARF-NEXT: line_range: 14 +; DWARF-NEXT: opcode_base: 13 +; DWARF-NEXT: standard_opcode_lengths[DW_LNS_copy] = 0 +; DWARF-NEXT: standard_opcode_lengths[DW_LNS_advance_pc] = 1 +; DWARF-NEXT: standard_opcode_lengths[DW_LNS_advance_line] = 1 +; DWARF-NEXT: standard_opcode_lengths[DW_LNS_set_file] = 1 +; DWARF-NEXT: standard_opcode_lengths[DW_LNS_set_column] = 1 +; DWARF-NEXT: standard_opcode_lengths[DW_LNS_negate_stmt] = 0 +; DWARF-NEXT: standard_opcode_lengths[DW_LNS_set_basic_block] = 0 +; DWARF-NEXT: standard_opcode_lengths[DW_LNS_const_add_pc] = 0 +; DWARF-NEXT: standard_opcode_lengths[DW_LNS_fixed_advance_pc] = 1 +; DWARF-NEXT: standard_opcode_lengths[DW_LNS_set_prologue_end] = 0 +; DWARF-NEXT: standard_opcode_lengths[DW_LNS_set_epilogue_begin] = 0 +; DWARF-NEXT: standard_opcode_lengths[DW_LNS_set_isa] = 1 +; DWARF-NEXT: include_directories[ 0] = "." +; DWARF-NEXT: file_names[ 0]: +; DWARF-NEXT: name: "dwarf-loongarch-relocs.c" +; DWARF-NEXT: dir_index: 0 +; DWARF-NEXT: md5_checksum: f44d6d71bc4da58b4abe338ca507c007 +; DWARF-NEXT: source: "{{.*}}" +; DWARF-EMPTY: +; DWARF-NEXT: Address Line Column File ISA Discriminator OpIndex Flags +; DWARF-NEXT: ------------------ ------ ------ ------ --- ------------- ------- ------------- +; DWARF-NORL-NEXT: 0x0000000000000000 2 0 0 0 0 0 is_stmt +; DWARF-NORL-NEXT: 0x0000000000000010 3 3 0 0 0 0 is_stmt prologue_end +; DWARF-NORL-NEXT: 0x0000000000000020 10 3 0 0 0 0 is_stmt +; DWARF-NORL-NEXT: 0x000000000000002c 10 3 0 0 0 0 epilogue_begin +; DWARF-NORL-NEXT: 0x0000000000000034 10 3 0 0 0 0 end_sequence +; DWARF-ENRL-NEXT: 0x000000000000001c 2 0 0 0 0 0 is_stmt +; DWARF-ENRL-NEXT: 0x000000000000002c 3 3 0 0 0 0 is_stmt prologue_end +; DWARF-ENRL-NEXT: 0x000000000000003c 10 3 0 0 0 0 is_stmt +; DWARF-ENRL-NEXT: 0x0000000000000048 10 3 0 0 0 0 epilogue_begin +; DWARF-ENRL-NEXT: 0x0000000000000050 10 3 0 0 0 0 end_sequence + +; ModuleID = 'dwarf-loongarch-relocs.c' +source_filename = "dwarf-loongarch-relocs.c" +target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128" +target triple = "loongarch64" + +; Function Attrs: noinline nounwind optnone +define dso_local signext i32 @foo() #0 !dbg !8 { + call void asm sideeffect ".cfi_remember_state\0A\09.cfi_adjust_cfa_offset 16\0A\09nop\0A\09la.pcrel $$t0, sym\0A\09nop\0A\09.cfi_restore_state\0A\09", ""() #1, !dbg !12, !srcloc !13 + ret i32 0, !dbg !14 +} + +attributes #0 = { noinline nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="loongarch64" "target-features"="+64bit,+d,+f,+ual" } +attributes #1 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "dwarf-loongarch-relocs.c", directory: ".", checksumkind: CSK_MD5, checksum: "f44d6d71bc4da58b4abe338ca507c007", source: "int foo()\0A{\0A asm volatile(\0A \22.cfi_remember_state\\n\\t\22\0A \22.cfi_adjust_cfa_offset 16\\n\\t\22\0A \22nop\\n\\t\22\0A \22la.pcrel $t0, sym\\n\\t\22\0A \22nop\\n\\t\22\0A \22.cfi_restore_state\\n\\t\22);\0A return 0;\0A}\0A") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"direct-access-external-data", i32 0} +!6 = !{i32 7, !"frame-pointer", i32 2} +!7 = !{!"clang"} +!8 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !9, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0) +!9 = !DISubroutineType(types: !10) +!10 = !{!11} +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !DILocation(line: 3, column: 3, scope: !8) +!13 = !{i64 34, i64 56, i64 92, i64 106, i64 134, i64 148, i64 177} +!14 = !DILocation(line: 10, column: 3, scope: !8) diff --git a/llvm/test/DebugInfo/LoongArch/lit.local.cfg b/llvm/test/DebugInfo/LoongArch/lit.local.cfg new file mode 100644 index 0000000000000000000000000000000000000000..77becb8eee90b0fdeff1d3e164cf4d5edadac024 --- /dev/null +++ b/llvm/test/DebugInfo/LoongArch/lit.local.cfg @@ -0,0 +1,2 @@ +if "LoongArch" not in config.root.targets: + config.unsupported = True diff --git a/llvm/test/DebugInfo/Mips/eh_frame.ll b/llvm/test/DebugInfo/Mips/eh_frame.ll index 506e5b87892b9832cf4c9ae2302b382872e28240..60d4dc76777ebd585f8e3eb0d045ac1131683617 100644 --- a/llvm/test/DebugInfo/Mips/eh_frame.ll +++ b/llvm/test/DebugInfo/Mips/eh_frame.ll @@ -26,9 +26,9 @@ ; CHECK-READELF-PIC-NEXT: R_MIPS_PC32 ; CHECK-READELF-NEXT: .gcc_except_table -; EXCEPT-TABLE-STATIC: 0000 ff9b1501 0c011500 00150e23 01231e00 ...........#.#.. +; EXCEPT-TABLE-STATIC: 0000 ff9b1501 0c001400 00140e22 01221e00 ...........".".. ; EXCEPT-TABLE-STATIC: 0010 00010000 00000000 -; EXCEPT-TABLE-PIC: 0000 ff9b1501 0c012d00 002d133f 013f2a00 ......-..-.?.?*. +; EXCEPT-TABLE-PIC: 0000 ff9b1501 0c002c00 002c123e 013e2a00 ......,..,.>.>*. ; EXCEPT-TABLE-PIC: 0010 00010000 00000000 ........ @_ZTIi = external constant ptr diff --git a/llvm/test/MC/ELF/RISCV/gen-dwarf.s b/llvm/test/MC/ELF/RISCV/gen-dwarf.s index 2235559d5f3575acd5b91a9ffa0c7da0e267a59c..2a7dc777e70c46e3aa0f0cde0970d939543162a3 100644 --- a/llvm/test/MC/ELF/RISCV/gen-dwarf.s +++ b/llvm/test/MC/ELF/RISCV/gen-dwarf.s @@ -9,7 +9,7 @@ ## emit special opcodes to make .debug_line smaller, but we don't do this for ## consistency. -# RUN: llvm-mc -filetype=obj -triple=riscv64 -g -dwarf-version=5 -mattr=+relax < %s -o %t +# RUN: llvm-mc -filetype=obj -triple=riscv64 -g -dwarf-version=5 -mattr=+relax -riscv-uleb128-reloc=1 < %s -o %t # RUN: llvm-dwarfdump -eh-frame -debug-line -debug-rnglists -v %t | FileCheck %s # RUN: llvm-readobj -r -x .eh_frame %t | FileCheck %s --check-prefix=RELOC @@ -48,9 +48,10 @@ # RELOC-NEXT: 0x34 R_RISCV_32_PCREL 0x0 # RELOC-NEXT: } -## TODO A section needs two relocations. # RELOC: Section ([[#]]) .rela.debug_rnglists { # RELOC-NEXT: 0xD R_RISCV_64 .text.foo 0x0 +# RELOC-NEXT: 0x15 R_RISCV_SET_ULEB128 0x0 +# RELOC-NEXT: 0x15 R_RISCV_SUB_ULEB128 .text.foo 0x0 # RELOC-NEXT: 0x17 R_RISCV_64 .text.bar 0x0 # RELOC-NEXT: } diff --git a/llvm/test/MC/ELF/uleb-ehtable.s b/llvm/test/MC/ELF/uleb-ehtable.s index ca3f9e97bffc245da170beb8705ab0ba765bcfdd..6407223f36e78e9dd009152d44d798539184216e 100644 --- a/llvm/test/MC/ELF/uleb-ehtable.s +++ b/llvm/test/MC/ELF/uleb-ehtable.s @@ -1,7 +1,7 @@ // RUN: llvm-mc -filetype=obj -triple i686-pc-linux-gnu %s -o - | llvm-readobj -S --sd - | FileCheck %s -check-prefix=CHECK -check-prefix=ELF // RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -S --sd - | FileCheck %s -check-prefix=CHECK -check-prefix=ELF -// RUN: llvm-mc -filetype=obj -triple i386-apple-darwin9 %s -o - | llvm-readobj -S --sd - | FileCheck %s -check-prefix=CHECK -check-prefix=MACHO -// RUN: llvm-mc -filetype=obj -triple x86_64-apple-darwin9 %s -o - | llvm-readobj -S --sd - | FileCheck %s -check-prefix=CHECK -check-prefix=MACHO +// RUN: llvm-mc -filetype=obj -triple i386-apple-darwin9 --defsym MACHO=1 %s -o - | llvm-readobj -S --sd - | FileCheck %s -check-prefix=CHECK -check-prefix=MACHO +// RUN: llvm-mc -filetype=obj -triple x86_64-apple-darwin9 --defsym MACHO=1 %s -o - | llvm-readobj -S --sd - | FileCheck %s -check-prefix=CHECK -check-prefix=MACHO // Test that we can assemble a GCC-like EH table that has 16381-16383 bytes of // non-padding data between .ttbaseref and .ttbase. The assembler must insert @@ -13,11 +13,20 @@ foo: .byte 0xff // LPStart omitted .byte 0x1 // TType encoding (uleb128) +.ifdef MACHO + .uleb128 Lttbase-Lttbaseref +Lttbaseref: +.else .uleb128 .ttbase-.ttbaseref .ttbaseref: +.endif .fill 128*128-1, 1, 0xcd // call site and actions tables .balign 4 +.ifdef MACHO +Lttbase: +.else .ttbase: +.endif .byte 1, 2, 3, 4 // ELF: Name: .data diff --git a/llvm/test/MC/LoongArch/Macros/macros-la.s b/llvm/test/MC/LoongArch/Macros/macros-la.s index 924e4326b8e5d66f8bd225548f0db4f6743c9c25..1a1d12d7d7dfd19214981df2af1e08964bbdbc53 100644 --- a/llvm/test/MC/LoongArch/Macros/macros-la.s +++ b/llvm/test/MC/LoongArch/Macros/macros-la.s @@ -1,66 +1,128 @@ # RUN: llvm-mc --triple=loongarch64 %s | FileCheck %s +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax %s -o %t +# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELOC +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o %t.relax +# RUN: llvm-readobj -r %t.relax | FileCheck %s --check-prefixes=RELOC,RELAX + +# RELOC: Relocations [ +# RELOC-NEXT: Section ({{.*}}) .rela.text { la.abs $a0, sym_abs # CHECK: lu12i.w $a0, %abs_hi20(sym_abs) # CHECK-NEXT: ori $a0, $a0, %abs_lo12(sym_abs) # CHECK-NEXT: lu32i.d $a0, %abs64_lo20(sym_abs) # CHECK-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym_abs) +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_ABS_HI20 sym_abs 0x0 +# RELOC-NEXT: R_LARCH_ABS_LO12 sym_abs 0x0 +# RELOC-NEXT: R_LARCH_ABS64_LO20 sym_abs 0x0 +# RELOC-NEXT: R_LARCH_ABS64_HI12 sym_abs 0x0 la.pcrel $a0, sym_pcrel -# CHECK: pcalau12i $a0, %pc_hi20(sym_pcrel) +# CHECK-NEXT: pcalau12i $a0, %pc_hi20(sym_pcrel) # CHECK-NEXT: addi.d $a0, $a0, %pc_lo12(sym_pcrel) +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_PCALA_HI20 sym_pcrel 0x0 +# RELAX-NEXT: R_LARCH_RELAX - 0x0 +# RELOC-NEXT: R_LARCH_PCALA_LO12 sym_pcrel 0x0 +# RELAX-NEXT: R_LARCH_RELAX - 0x0 la.pcrel $a0, $a1, sym_pcrel_large -# CHECK: pcalau12i $a0, %pc_hi20(sym_pcrel_large) +# CHECK-NEXT: pcalau12i $a0, %pc_hi20(sym_pcrel_large) # CHECK-NEXT: addi.d $a1, $zero, %pc_lo12(sym_pcrel_large) # CHECK-NEXT: lu32i.d $a1, %pc64_lo20(sym_pcrel_large) # CHECK-NEXT: lu52i.d $a1, $a1, %pc64_hi12(sym_pcrel_large) # CHECK-NEXT: add.d $a0, $a0, $a1 +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_PCALA_HI20 sym_pcrel_large 0x0 +# RELOC-NEXT: R_LARCH_PCALA_LO12 sym_pcrel_large 0x0 +# RELOC-NEXT: R_LARCH_PCALA64_LO20 sym_pcrel_large 0x0 +# RELOC-NEXT: R_LARCH_PCALA64_HI12 sym_pcrel_large 0x0 la.got $a0, sym_got -# CHECK: pcalau12i $a0, %got_pc_hi20(sym_got) +# CHECK-NEXT: pcalau12i $a0, %got_pc_hi20(sym_got) # CHECK-NEXT: ld.d $a0, $a0, %got_pc_lo12(sym_got) +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_GOT_PC_HI20 sym_got 0x0 +# RELAX-NEXT: R_LARCH_RELAX - 0x0 +# RELOC-NEXT: R_LARCH_GOT_PC_LO12 sym_got 0x0 +# RELAX-NEXT: R_LARCH_RELAX - 0x0 la.got $a0, $a1, sym_got_large -# CHECK: pcalau12i $a0, %got_pc_hi20(sym_got_large) +# CHECK-NEXT: pcalau12i $a0, %got_pc_hi20(sym_got_large) # CHECK-NEXT: addi.d $a1, $zero, %got_pc_lo12(sym_got_large) # CHECK-NEXT: lu32i.d $a1, %got64_pc_lo20(sym_got_large) # CHECK-NEXT: lu52i.d $a1, $a1, %got64_pc_hi12(sym_got_large) # CHECK-NEXT: ldx.d $a0, $a0, $a1 +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_GOT_PC_HI20 sym_got_large 0x0 +# RELOC-NEXT: R_LARCH_GOT_PC_LO12 sym_got_large 0x0 +# RELOC-NEXT: R_LARCH_GOT64_PC_LO20 sym_got_large 0x0 +# RELOC-NEXT: R_LARCH_GOT64_PC_HI12 sym_got_large 0x0 la.tls.le $a0, sym_le -# CHECK: lu12i.w $a0, %le_hi20(sym_le) +# CHECK-NEXT: lu12i.w $a0, %le_hi20(sym_le) # CHECK-NEXT: ori $a0, $a0, %le_lo12(sym_le) +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_TLS_LE_HI20 sym_le 0x0 +# RELOC-NEXT: R_LARCH_TLS_LE_LO12 sym_le 0x0 la.tls.ie $a0, sym_ie -# CHECK: pcalau12i $a0, %ie_pc_hi20(sym_ie) +# CHECK-NEXT: pcalau12i $a0, %ie_pc_hi20(sym_ie) # CHECK-NEXT: ld.d $a0, $a0, %ie_pc_lo12(sym_ie) +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_TLS_IE_PC_HI20 sym_ie 0x0 +# RELOC-NEXT: R_LARCH_TLS_IE_PC_LO12 sym_ie 0x0 la.tls.ie $a0, $a1, sym_ie_large -# CHECK: pcalau12i $a0, %ie_pc_hi20(sym_ie_large) +# CHECK-NEXT: pcalau12i $a0, %ie_pc_hi20(sym_ie_large) # CHECK-NEXT: addi.d $a1, $zero, %ie_pc_lo12(sym_ie_large) # CHECK-NEXT: lu32i.d $a1, %ie64_pc_lo20(sym_ie_large) # CHECK-NEXT: lu52i.d $a1, $a1, %ie64_pc_hi12(sym_ie_large) # CHECK-NEXT: ldx.d $a0, $a0, $a1 +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_TLS_IE_PC_HI20 sym_ie_large 0x0 +# RELOC-NEXT: R_LARCH_TLS_IE_PC_LO12 sym_ie_large 0x0 +# RELOC-NEXT: R_LARCH_TLS_IE64_PC_LO20 sym_ie_large 0x0 +# RELOC-NEXT: R_LARCH_TLS_IE64_PC_HI12 sym_ie_large 0x0 la.tls.ld $a0, sym_ld -# CHECK: pcalau12i $a0, %ld_pc_hi20(sym_ld) +# CHECK-NEXT: pcalau12i $a0, %ld_pc_hi20(sym_ld) # CHECK-NEXT: addi.d $a0, $a0, %got_pc_lo12(sym_ld) +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_TLS_LD_PC_HI20 sym_ld 0x0 +# RELOC-NEXT: R_LARCH_GOT_PC_LO12 sym_ld 0x0 la.tls.ld $a0, $a1, sym_ld_large -# CHECK: pcalau12i $a0, %ld_pc_hi20(sym_ld_large) +# CHECK-NEXT: pcalau12i $a0, %ld_pc_hi20(sym_ld_large) # CHECK-NEXT: addi.d $a1, $zero, %got_pc_lo12(sym_ld_large) # CHECK-NEXT: lu32i.d $a1, %got64_pc_lo20(sym_ld_large) # CHECK-NEXT: lu52i.d $a1, $a1, %got64_pc_hi12(sym_ld_large) # CHECK-NEXT: add.d $a0, $a0, $a1 +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_TLS_LD_PC_HI20 sym_ld_large 0x0 +# RELOC-NEXT: R_LARCH_GOT_PC_LO12 sym_ld_large 0x0 +# RELOC-NEXT: R_LARCH_GOT64_PC_LO20 sym_ld_large 0x0 +# RELOC-NEXT: R_LARCH_GOT64_PC_HI12 sym_ld_large 0x0 la.tls.gd $a0, sym_gd -# CHECK: pcalau12i $a0, %gd_pc_hi20(sym_gd) +# CHECK-NEXT: pcalau12i $a0, %gd_pc_hi20(sym_gd) # CHECK-NEXT: addi.d $a0, $a0, %got_pc_lo12(sym_gd) +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_TLS_GD_PC_HI20 sym_gd 0x0 +# RELOC-NEXT: R_LARCH_GOT_PC_LO12 sym_gd 0x0 la.tls.gd $a0, $a1, sym_gd_large -# CHECK: pcalau12i $a0, %gd_pc_hi20(sym_gd_large) +# CHECK-NEXT: pcalau12i $a0, %gd_pc_hi20(sym_gd_large) # CHECK-NEXT: addi.d $a1, $zero, %got_pc_lo12(sym_gd_large) # CHECK-NEXT: lu32i.d $a1, %got64_pc_lo20(sym_gd_large) # CHECK-NEXT: lu52i.d $a1, $a1, %got64_pc_hi12(sym_gd_large) # CHECK-NEXT: add.d $a0, $a0, $a1 +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_TLS_GD_PC_HI20 sym_gd_large 0x0 +# RELOC-NEXT: R_LARCH_GOT_PC_LO12 sym_gd_large 0x0 +# RELOC-NEXT: R_LARCH_GOT64_PC_LO20 sym_gd_large 0x0 +# RELOC-NEXT: R_LARCH_GOT64_PC_HI12 sym_gd_large 0x0 + +# RELOC-NEXT: } +# RELOC-NEXT: ] diff --git a/llvm/test/MC/LoongArch/Misc/cfi-advance.s b/llvm/test/MC/LoongArch/Misc/cfi-advance.s new file mode 100644 index 0000000000000000000000000000000000000000..662c43e6bceafd5f855c1acccba1326fe4518159 --- /dev/null +++ b/llvm/test/MC/LoongArch/Misc/cfi-advance.s @@ -0,0 +1,27 @@ +# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=-relax %s -o %t.o +# RUN: llvm-readobj -r %t.o | FileCheck --check-prefix=RELOC %s +# RUN: llvm-dwarfdump --debug-frame %t.o | FileCheck --check-prefix=DWARFDUMP %s + +# RELOC: Relocations [ +# RELOC-NEXT: .rela.eh_frame { +# RELOC-NEXT: 0x1C R_LARCH_32_PCREL .text 0x0 +# RELOC-NEXT: } +# RELOC-NEXT: ] +# DWARFDUMP: DW_CFA_advance_loc: 4 +# DWARFDUMP-NEXT: DW_CFA_def_cfa_offset: +8 +# DWARFDUMP-NEXT: DW_CFA_advance_loc: 8 +# DWARFDUMP-NEXT: DW_CFA_def_cfa_offset: +8 + + .text + .globl test + .p2align 2 + .type test,@function +test: + .cfi_startproc + nop + .cfi_def_cfa_offset 8 + .p2align 3 + nop + .cfi_def_cfa_offset 8 + nop + .cfi_endproc diff --git a/llvm/test/MC/LoongArch/Misc/subsection.s b/llvm/test/MC/LoongArch/Misc/subsection.s new file mode 100644 index 0000000000000000000000000000000000000000..566a2408d6913de3c9d77bb1eca1f8fa17f4b183 --- /dev/null +++ b/llvm/test/MC/LoongArch/Misc/subsection.s @@ -0,0 +1,38 @@ +# RUN: not llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax %s -o /dev/null 2>&1 | FileCheck %s --check-prefixes=ERR,NORELAX --implicit-check-not=error: +# RUN: not llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o /dev/null 2>&1 | FileCheck %s --check-prefixes=ERR,RELAX --implicit-check-not=error: + +a: + nop +b: + la.pcrel $t0, a +c: + nop +d: + +.data +## Positive subsection numbers +## With relaxation, report an error as c-b is not an assemble-time constant. +# RELAX: :[[#@LINE+1]]:14: error: cannot evaluate subsection number +.subsection c-b +# RELAX: :[[#@LINE+1]]:14: error: cannot evaluate subsection number +.subsection d-b +# RELAX: :[[#@LINE+1]]:14: error: cannot evaluate subsection number +.subsection c-a + +.subsection b-a +.subsection d-c + +## Negative subsection numbers +# NORELAX: :[[#@LINE+2]]:14: error: subsection number -8 is not within [0,2147483647] +# RELAX: :[[#@LINE+1]]:14: error: cannot evaluate subsection number +.subsection b-c +# NORELAX: :[[#@LINE+2]]:14: error: subsection number -12 is not within [0,2147483647] +# RELAX: :[[#@LINE+1]]:14: error: cannot evaluate subsection number +.subsection b-d +# NORELAX: :[[#@LINE+2]]:14: error: subsection number -12 is not within [0,2147483647] +# RELAX: :[[#@LINE+1]]:14: error: cannot evaluate subsection number +.subsection a-c +# ERR: :[[#@LINE+1]]:14: error: subsection number -4 is not within [0,2147483647] +.subsection a-b +# ERR: :[[#@LINE+1]]:14: error: subsection number -4 is not within [0,2147483647] +.subsection c-d diff --git a/llvm/test/MC/LoongArch/Relocations/align-non-executable.s b/llvm/test/MC/LoongArch/Relocations/align-non-executable.s new file mode 100644 index 0000000000000000000000000000000000000000..47834acd9521fe17e3a81bdd22722e668ce4ac11 --- /dev/null +++ b/llvm/test/MC/LoongArch/Relocations/align-non-executable.s @@ -0,0 +1,27 @@ +## A label difference separated by an alignment directive, when the +## referenced symbols are in a non-executable section with instructions, +## should generate ADD/SUB relocations. +## https://github.com/llvm/llvm-project/pull/76552 + +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s \ +# RUN: | llvm-readobj -r - | FileCheck --check-prefixes=CHECK,RELAX %s +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax %s \ +# RUN: | llvm-readobj -r - | FileCheck %s + +.section ".dummy", "a" +.L1: + la.pcrel $t0, sym +.p2align 3 +.L2: +.dword .L2 - .L1 + +# CHECK: Relocations [ +# CHECK-NEXT: Section ({{.*}}) .rela.dummy { +# CHECK-NEXT: 0x0 R_LARCH_PCALA_HI20 sym 0x0 +# RELAX-NEXT: 0x0 R_LARCH_RELAX - 0x0 +# CHECK-NEXT: 0x4 R_LARCH_PCALA_LO12 sym 0x0 +# RELAX-NEXT: 0x4 R_LARCH_RELAX - 0x0 +# RELAX-NEXT: 0x8 R_LARCH_ADD64 .L2 0x0 +# RELAX-NEXT: 0x8 R_LARCH_SUB64 .L1 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] diff --git a/llvm/test/MC/LoongArch/Relocations/leb128.s b/llvm/test/MC/LoongArch/Relocations/leb128.s new file mode 100644 index 0000000000000000000000000000000000000000..7a96ec551b76ba66c75bbe94e8ea10b9d8894251 --- /dev/null +++ b/llvm/test/MC/LoongArch/Relocations/leb128.s @@ -0,0 +1,72 @@ +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax %s -o %t +# RUN: llvm-readobj -r -x .alloc_w %t | FileCheck --check-prefixes=CHECK,NORELAX %s +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o %t.relax +# RUN: llvm-readobj -r -x .alloc_w %t.relax | FileCheck --check-prefixes=CHECK,RELAX %s + +# RUN: not llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax --defsym ERR=1 %s -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERR +# RUN: not llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax --defsym ERR=1 %s -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERR + +# CHECK: Relocations [ +# CHECK-NEXT: .rela.alloc_w { +# RELAX-NEXT: 0x0 R_LARCH_ADD_ULEB128 w1 0x0 +# RELAX-NEXT: 0x0 R_LARCH_SUB_ULEB128 w 0x0 +# RELAX-NEXT: 0x1 R_LARCH_ADD_ULEB128 w2 0x0 +# RELAX-NEXT: 0x1 R_LARCH_SUB_ULEB128 w1 0x0 +# CHECK-NEXT: 0x2 R_LARCH_PCALA_HI20 foo 0x0 +# RELAX-NEXT: 0x2 R_LARCH_RELAX - 0x0 +# CHECK-NEXT: 0x6 R_LARCH_PCALA_LO12 foo 0x0 +# RELAX-NEXT: 0x6 R_LARCH_RELAX - 0x0 +# RELAX-NEXT: 0xA R_LARCH_ADD_ULEB128 w2 0x0 +# RELAX-NEXT: 0xA R_LARCH_SUB_ULEB128 w1 0x0 +# RELAX-NEXT: 0xB R_LARCH_ADD_ULEB128 w2 0x78 +# RELAX-NEXT: 0xB R_LARCH_SUB_ULEB128 w1 0x0 +# RELAX-NEXT: 0xD R_LARCH_ADD_ULEB128 w1 0x0 +# RELAX-NEXT: 0xD R_LARCH_SUB_ULEB128 w2 0x0 +# RELAX-NEXT: 0x17 R_LARCH_ADD_ULEB128 w3 0x6F +# RELAX-NEXT: 0x17 R_LARCH_SUB_ULEB128 w2 0x0 +# RELAX-NEXT: 0x18 R_LARCH_ADD_ULEB128 w3 0x71 +# RELAX-NEXT: 0x18 R_LARCH_SUB_ULEB128 w2 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: Hex dump of section '.alloc_w': +# NORELAX-NEXT: 0x00000000 02080c00 001a8c01 c0020880 01f8ffff +# NORELAX-NEXT: 0x00000010 ffffffff ffff017f 8101 +# RELAX-NEXT: 0x00000000 00000c00 001a8c01 c0020080 00808080 +# RELAX-NEXT: 0x00000010 80808080 80800000 8000 + +.section .alloc_w,"ax",@progbits; w: +.uleb128 w1-w # w1 is later defined in the same section +.uleb128 w2-w1 # w1 and w2 are separated by a linker relaxable instruction +w1: + la.pcrel $t0, foo +w2: +.uleb128 w2-w1 # 0x08 +.uleb128 w2-w1+120 # 0x0180 +.uleb128 -(w2-w1) # 0x01fffffffffffffffff8 +.uleb128 w3-w2+111 # 0x7f +.uleb128 w3-w2+113 # 0x0181 +w3: + +.ifdef ERR +# ERR: :[[#@LINE+1]]:16: error: .uleb128 expression is not absolute +.uleb128 extern-w # extern is undefined +# ERR: :[[#@LINE+1]]:11: error: .uleb128 expression is not absolute +.uleb128 w-extern +# ERR: :[[#@LINE+1]]:11: error: .uleb128 expression is not absolute +.uleb128 x-w # x is later defined in another section + +.section .alloc_x,"aw",@progbits; x: +# ERR: :[[#@LINE+1]]:11: error: .uleb128 expression is not absolute +.uleb128 y-x +.section .alloc_y,"aw",@progbits; y: +# ERR: :[[#@LINE+1]]:11: error: .uleb128 expression is not absolute +.uleb128 x-y + +# ERR: :[[#@LINE+1]]:10: error: .uleb128 expression is not absolute +.uleb128 extern +# ERR: :[[#@LINE+1]]:10: error: .uleb128 expression is not absolute +.uleb128 y +.endif diff --git a/llvm/test/MC/LoongArch/Relocations/relax-addsub.s b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s new file mode 100644 index 0000000000000000000000000000000000000000..18e0ede5e29375264dfb6a3ac8efb535fb8b2c67 --- /dev/null +++ b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s @@ -0,0 +1,107 @@ +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax %s \ +# RUN: | llvm-readobj -r -x .data - | FileCheck %s --check-prefix=NORELAX +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s \ +# RUN: | llvm-readobj -r -x .data - | FileCheck %s --check-prefix=RELAX + +# NORELAX: Relocations [ +# NORELAX-NEXT: Section ({{.*}}) .rela.text { +# NORELAX-NEXT: 0x10 R_LARCH_PCALA_HI20 .text 0x0 +# NORELAX-NEXT: 0x14 R_LARCH_PCALA_LO12 .text 0x0 +# NORELAX-NEXT: } +# NORELAX-NEXT: Section ({{.*}}) .rela.data { +# NORELAX-NEXT: 0x30 R_LARCH_ADD8 foo 0x0 +# NORELAX-NEXT: 0x30 R_LARCH_SUB8 .text 0x10 +# NORELAX-NEXT: 0x31 R_LARCH_ADD16 foo 0x0 +# NORELAX-NEXT: 0x31 R_LARCH_SUB16 .text 0x10 +# NORELAX-NEXT: 0x33 R_LARCH_ADD32 foo 0x0 +# NORELAX-NEXT: 0x33 R_LARCH_SUB32 .text 0x10 +# NORELAX-NEXT: 0x37 R_LARCH_ADD64 foo 0x0 +# NORELAX-NEXT: 0x37 R_LARCH_SUB64 .text 0x10 +# NORELAX-NEXT: } +# NORELAX-NEXT: ] + +# NORELAX: Hex dump of section '.data': +# NORELAX-NEXT: 0x00000000 04040004 00000004 00000000 00000004 +# NORELAX-NEXT: 0x00000010 0c0c000c 0000000c 00000000 0000000c +# NORELAX-NEXT: 0x00000020 08080008 00000008 00000000 00000008 +# NORELAX-NEXT: 0x00000030 00000000 00000000 00000000 000000 + +# RELAX: Relocations [ +# RELAX-NEXT: Section ({{.*}}) .rela.text { +# RELAX-NEXT: 0x4 R_LARCH_ALIGN {{.*}} 0x4 +# RELAX-NEXT: 0x10 R_LARCH_PCALA_HI20 .L1 0x0 +# RELAX-NEXT: 0x10 R_LARCH_RELAX - 0x0 +# RELAX-NEXT: 0x14 R_LARCH_PCALA_LO12 .L1 0x0 +# RELAX-NEXT: 0x14 R_LARCH_RELAX - 0x0 +# RELAX-NEXT: } +# RELAX-NEXT: Section ({{.*}}) .rela.data { +# RELAX-NEXT: 0x10 R_LARCH_ADD8 .L3 0x0 +# RELAX-NEXT: 0x10 R_LARCH_SUB8 .L2 0x0 +# RELAX-NEXT: 0x11 R_LARCH_ADD16 .L3 0x0 +# RELAX-NEXT: 0x11 R_LARCH_SUB16 .L2 0x0 +# RELAX-NEXT: 0x13 R_LARCH_ADD32 .L3 0x0 +# RELAX-NEXT: 0x13 R_LARCH_SUB32 .L2 0x0 +# RELAX-NEXT: 0x17 R_LARCH_ADD64 .L3 0x0 +# RELAX-NEXT: 0x17 R_LARCH_SUB64 .L2 0x0 +# RELAX-NEXT: 0x1F R_LARCH_ADD_ULEB128 .L3 0x0 +# RELAX-NEXT: 0x1F R_LARCH_SUB_ULEB128 .L2 0x0 +# RELAX-NEXT: 0x20 R_LARCH_ADD8 .L4 0x0 +# RELAX-NEXT: 0x20 R_LARCH_SUB8 .L3 0x0 +# RELAX-NEXT: 0x21 R_LARCH_ADD16 .L4 0x0 +# RELAX-NEXT: 0x21 R_LARCH_SUB16 .L3 0x0 +# RELAX-NEXT: 0x23 R_LARCH_ADD32 .L4 0x0 +# RELAX-NEXT: 0x23 R_LARCH_SUB32 .L3 0x0 +# RELAX-NEXT: 0x27 R_LARCH_ADD64 .L4 0x0 +# RELAX-NEXT: 0x27 R_LARCH_SUB64 .L3 0x0 +# RELAX-NEXT: 0x2F R_LARCH_ADD_ULEB128 .L4 0x0 +# RELAX-NEXT: 0x2F R_LARCH_SUB_ULEB128 .L3 0x0 +# RELAX-NEXT: 0x30 R_LARCH_ADD8 foo 0x0 +# RELAX-NEXT: 0x30 R_LARCH_SUB8 .L3 0x0 +# RELAX-NEXT: 0x31 R_LARCH_ADD16 foo 0x0 +# RELAX-NEXT: 0x31 R_LARCH_SUB16 .L3 0x0 +# RELAX-NEXT: 0x33 R_LARCH_ADD32 foo 0x0 +# RELAX-NEXT: 0x33 R_LARCH_SUB32 .L3 0x0 +# RELAX-NEXT: 0x37 R_LARCH_ADD64 foo 0x0 +# RELAX-NEXT: 0x37 R_LARCH_SUB64 .L3 0x0 +# RELAX-NEXT: } +# RELAX-NEXT: ] + +# RELAX: Hex dump of section '.data': +# RELAX-NEXT: 0x00000000 04040004 00000004 00000000 00000004 +# RELAX-NEXT: 0x00000010 00000000 00000000 00000000 00000000 +# RELAX-NEXT: 0x00000020 00000000 00000000 00000000 00000000 +# RELAX-NEXT: 0x00000030 00000000 00000000 00000000 000000 + +.text +.L1: + nop +.L2: + .align 4 +.L3: + la.pcrel $t0, .L1 +.L4: + ret + +.data +## Not emit relocs +.byte .L2 - .L1 +.short .L2 - .L1 +.word .L2 - .L1 +.dword .L2 - .L1 +.uleb128 .L2 - .L1 +## With relaxation, emit relocs because the .align makes the diff variable. +.byte .L3 - .L2 +.short .L3 - .L2 +.word .L3 - .L2 +.dword .L3 - .L2 +.uleb128 .L3 - .L2 +## With relaxation, emit relocs because the la.pcrel makes the diff variable. +.byte .L4 - .L3 +.short .L4 - .L3 +.word .L4 - .L3 +.dword .L4 - .L3 +.uleb128 .L4 - .L3 +.byte foo - .L3 +.short foo - .L3 +.word foo - .L3 +.dword foo - .L3 diff --git a/llvm/test/MC/LoongArch/Relocations/relax-align.s b/llvm/test/MC/LoongArch/Relocations/relax-align.s new file mode 100644 index 0000000000000000000000000000000000000000..294fd9fb916c75a14f240c3d05c3aedd4a72a2c1 --- /dev/null +++ b/llvm/test/MC/LoongArch/Relocations/relax-align.s @@ -0,0 +1,79 @@ +## The file testing Nop insertion with R_LARCH_ALIGN for relaxation. + +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax %s -o %t +# RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=INSTR +# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELOC +# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o %t.r +# RUN: llvm-objdump -d %t.r | FileCheck %s --check-prefixes=INSTR,RELAX-INSTR +# RUN: llvm-readobj -r %t.r | FileCheck %s --check-prefixes=RELOC,RELAX-RELOC + +.text +break 0 +# INSTR: break 0 + +## Not emit R_LARCH_ALIGN if alignment directive is less than or equal to +## minimum code alignment(a.k.a 4). +.p2align 2 +.p2align 1 +.p2align 0 + +## Not emit instructions if max emit bytes less than min nop size. +.p2align 4, , 2 + +## Not emit R_LARCH_ALIGN if alignment directive with specific padding value. +## The behavior is the same as GNU assembler. +break 1 +.p2align 4, 1 +# INSTR-NEXT: break 1 +# INSTR-COUNT-2: 01 01 01 01 + +break 2 +.p2align 4, 1, 12 +# INSTR-NEXT: break 2 +# INSTR-COUNT-3: 01 01 01 01 + +break 3 +.p2align 4 +# INSTR-NEXT: break 3 +# INSTR-COUNT-3: nop + +break 4 +.p2align 5 +.p2align 4 +# INSTR-NEXT: break 4 +# INSTR-COUNT-3: nop +# RELAX-INSTR-COUNT-7: nop + +break 5 +.p2align 4, , 11 +# INSTR-NEXT: break 5 +# RELAX-INSTR-COUNT-3: nop + +break 6 +## Not emit the third parameter. +.p2align 4, , 12 +# INSTR-NEXT: break 6 +# INSTR-NEXT: nop +# INSTR-NEXT: nop +# RELAX-INSTR-NEXT: nop + +ret +# INSNR-NEXT: ret + +## Test the symbol index is different from .text. +.section .text2, "ax" +.p2align 4 +break 7 + +# RELOC: Relocations [ +# RELAX-RELOC-NEXT: Section ({{.*}}) .rela.text { +# RELAX-RELOC-NEXT: 0x24 R_LARCH_ALIGN .Lla-relax-align0 0x4 +# RELAX-RELOC-NEXT: 0x34 R_LARCH_ALIGN .Lla-relax-align0 0x5 +# RELAX-RELOC-NEXT: 0x50 R_LARCH_ALIGN .Lla-relax-align0 0x4 +# RELAX-RELOC-NEXT: 0x60 R_LARCH_ALIGN .Lla-relax-align0 0xB04 +# RELAX-RELOC-NEXT: 0x70 R_LARCH_ALIGN .Lla-relax-align0 0x4 +# RELAX-RELOC-NEXT: } +# RELAX-RELOC-NEXT: Section ({{.*}}) .rela.text2 { +# RELAX-RELOC-NEXT: 0x0 R_LARCH_ALIGN .Lla-relax-align1 0x4 +# RELAX-RELOC-NEXT: } +# RELOC-NEXT: ] diff --git a/llvm/test/MC/LoongArch/Relocations/relax-attr.s b/llvm/test/MC/LoongArch/Relocations/relax-attr.s new file mode 100644 index 0000000000000000000000000000000000000000..b1e648d850bb9089474b3179ec43028e796a0e63 --- /dev/null +++ b/llvm/test/MC/LoongArch/Relocations/relax-attr.s @@ -0,0 +1,32 @@ +# RUN: llvm-mc --filetype=obj --triple=loongarch64 %s -o %t +# RUN: llvm-readobj -r %t | FileCheck %s +# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax %s -o %t +# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=CHECKR + +# CHECK: Relocations [ +# CHECK-NEXT: Section ({{.*}}) .rela.data { +# CHECK-NEXT: 0x0 R_LARCH_64 .text 0x4 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECKR: Relocations [ +# CHECKR-NEXT: Section ({{.*}}) .rela.text { +# CHECKR-NEXT: 0x8 R_LARCH_B21 .L1 0x0 +# CHECKR-NEXT: 0xC R_LARCH_B16 .L1 0x0 +# CHECKR-NEXT: 0x10 R_LARCH_B26 .L1 0x0 +# CHECKR-NEXT: } +# CHECKR-NEXT: Section ({{.*}}) .rela.data { +# CHECKR-NEXT: 0x0 R_LARCH_64 .L1 0x0 +# CHECKR-NEXT: } +# CHECKR-NEXT: ] + +.text + nop +.L1: + nop + beqz $a0, .L1 + blt $a0, $a1, .L1 + b .L1 + +.data +.dword .L1 diff --git a/llvm/test/MC/X86/invalid-sleb.s b/llvm/test/MC/X86/invalid-sleb.s deleted file mode 100644 index 7d7df351ce4e24890990f809eea3f2cf49f99a3f..0000000000000000000000000000000000000000 --- a/llvm/test/MC/X86/invalid-sleb.s +++ /dev/null @@ -1,5 +0,0 @@ -// RUN: not --crash llvm-mc -filetype=obj -triple x86_64-pc-linux %s -o %t 2>&1 | FileCheck %s - -// CHECK: sleb128 and uleb128 expressions must be absolute - - .sleb128 undefined