From 3e065e152fffafa0c9865bae262fbcd62cf569e7 Mon Sep 17 00:00:00 2001 From: Jinyang He Date: Wed, 15 Nov 2023 09:57:45 +0800 Subject: [PATCH 01/14] [lld][LoongArch] Support the R_LARCH_{ADD,SUB}6 relocation type (#72190) The R_LARCH_{ADD,SUB}6 relocation type are usually used by DwarfCFA to calculate a tiny offset. They appear after binutils 2.41, with GAS enabling relaxation by default. (cherry picked from commit 72accbfd0a1023b3182202276904120524ff9200) --- lld/ELF/Arch/LoongArch.cpp | 8 ++++++++ lld/test/ELF/loongarch-add-sub.s | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp index 04ddb4682917..d3a538577a59 100644 --- a/lld/ELF/Arch/LoongArch.cpp +++ b/lld/ELF/Arch/LoongArch.cpp @@ -444,10 +444,12 @@ 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_SUB6: case R_LARCH_SUB8: case R_LARCH_SUB16: case R_LARCH_SUB32: @@ -650,6 +652,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 +667,9 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel, case R_LARCH_ADD64: write64le(loc, read64le(loc) + val); return; + case R_LARCH_SUB6: + *loc = (*loc & 0xc0) | ((*loc - val) & 0x3f); + return; case R_LARCH_SUB8: *loc -= val; return; diff --git a/lld/test/ELF/loongarch-add-sub.s b/lld/test/ELF/loongarch-add-sub.s index 63a3f7de179e..35f8a053d69c 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 -- Gitee From e820b025cefc41fb7d30c6ded98e34ad9d680dd9 Mon Sep 17 00:00:00 2001 From: Jinyang He Date: Thu, 16 Nov 2023 11:01:26 +0800 Subject: [PATCH 02/14] [LoongArch] Add relax feature and keep relocations (#72191) Add relax feature. To support linker relocation, we should make relocation with a symbol rather than section plus offset, and keep all relocations with non-abs symbol. (cherry picked from commit f5bfc833fcbf17a5876911783d1adaca7028d20c) --- llvm/lib/Target/LoongArch/LoongArch.td | 4 +++ .../lib/Target/LoongArch/LoongArchSubtarget.h | 2 ++ .../MCTargetDesc/LoongArchAsmBackend.cpp | 5 +-- .../MCTargetDesc/LoongArchELFObjectWriter.cpp | 18 ++++++++--- .../MCTargetDesc/LoongArchMCTargetDesc.h | 2 +- .../MC/LoongArch/Relocations/relax-attr.s | 32 +++++++++++++++++++ 6 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 llvm/test/MC/LoongArch/Relocations/relax-attr.s diff --git a/llvm/lib/Target/LoongArch/LoongArch.td b/llvm/lib/Target/LoongArch/LoongArch.td index 0675caa3b601..75b65fe69f26 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 0fbe23f2f62d..5c173675cca4 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 ecb68ff401e9..aae3e544d326 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp @@ -168,7 +168,7 @@ 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: @@ -193,7 +193,8 @@ bool LoongArchAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, 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/LoongArchELFObjectWriter.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFObjectWriter.cpp index a6b9c0652639..e60b9c2cfd97 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/LoongArchMCTargetDesc.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCTargetDesc.h index ab35a0096c8a..bb05baa9b717 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/test/MC/LoongArch/Relocations/relax-attr.s b/llvm/test/MC/LoongArch/Relocations/relax-attr.s new file mode 100644 index 000000000000..b1e648d850bb --- /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 -- Gitee From cbbdbbc5e16767a794f9f23b5d5ce657125bba52 Mon Sep 17 00:00:00 2001 From: Jinyang He Date: Wed, 20 Dec 2023 10:54:51 +0800 Subject: [PATCH 03/14] [LoongArch] Allow delayed decision for ADD/SUB relocations (#72960) Refer to RISCV [1], LoongArch also need delayed decision for ADD/SUB relocations. In handleAddSubRelocations, just return directly if SecA != SecB, handleFixup usually will finish the the rest of creating PCRel relocations works. Otherwise we emit relocs depends on whether relaxation is enabled. If not, we return true and avoid record ADD/SUB relocations. Now the two symbols separated by alignment directive will return without folding symbol offset in AttemptToFoldSymbolOffsetDifference, which has the same effect when relaxation is enabled. [1] https://reviews.llvm.org/D155357 (cherry picked from commit a8081ed8ff0fd11fb8d5f4c83df49da909e49612) --- llvm/lib/MC/MCExpr.cpp | 3 +- .../MCTargetDesc/LoongArchAsmBackend.cpp | 78 +++++++++++++++++++ .../MCTargetDesc/LoongArchAsmBackend.h | 9 ++- .../MCTargetDesc/LoongArchFixupKinds.h | 4 +- llvm/test/MC/LoongArch/Misc/subsection.s | 38 +++++++++ .../MC/LoongArch/Relocations/relax-addsub.s | 68 ++++++++++++++++ 6 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 llvm/test/MC/LoongArch/Misc/subsection.s create mode 100644 llvm/test/MC/LoongArch/Relocations/relax-addsub.s diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp index a7b980553af0..5a6596f93824 100644 --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -635,7 +635,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()) { diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp index aae3e544d326..1ed047a8e632 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp @@ -177,6 +177,34 @@ bool LoongArchAsmBackend::shouldForceRelocation(const MCAssembler &Asm, } } +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)); + } +} + 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,6 +219,56 @@ 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 MCSection &SecA = Target.getSymA()->getSymbol().getSection(); + const MCSection &SecB = Target.getSymB()->getSymbol().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 + // calculated 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; + 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( diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h index ae9bb8af0419..20f25b5cf53b 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h @@ -31,10 +31,15 @@ class LoongArchAsmBackend : public MCAsmBackend { 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, diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h index ba2d6718cdf9..178fa6e5262b 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h @@ -106,7 +106,9 @@ 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 }; } // end namespace LoongArch } // end namespace llvm diff --git a/llvm/test/MC/LoongArch/Misc/subsection.s b/llvm/test/MC/LoongArch/Misc/subsection.s new file mode 100644 index 000000000000..0bd22b474536 --- /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: +## TODO: 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/relax-addsub.s b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s new file mode 100644 index 000000000000..532eb4e0561a --- /dev/null +++ b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s @@ -0,0 +1,68 @@ +# 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: ] + +# NORELAX: Hex dump of section '.data': +# NORELAX-NEXT: 0x00000000 04040004 00000004 00000000 0000000c +# NORELAX-NEXT: 0x00000010 0c000c00 00000c00 00000000 00000808 +# NORELAX-NEXT: 0x00000020 00080000 00080000 00000000 00 + +# RELAX: Relocations [ +# RELAX-NEXT: Section ({{.*}}) .rela.text { +# RELAX-NEXT: 0x10 R_LARCH_PCALA_HI20 .L1 0x0 +# RELAX-NEXT: 0x14 R_LARCH_PCALA_LO12 .L1 0x0 +# RELAX-NEXT: } +# RELAX-NEXT: Section ({{.*}}) .rela.data { +# RELAX-NEXT: 0xF R_LARCH_ADD8 .L3 0x0 +# RELAX-NEXT: 0xF R_LARCH_SUB8 .L2 0x0 +# RELAX-NEXT: 0x10 R_LARCH_ADD16 .L3 0x0 +# RELAX-NEXT: 0x10 R_LARCH_SUB16 .L2 0x0 +# RELAX-NEXT: 0x12 R_LARCH_ADD32 .L3 0x0 +# RELAX-NEXT: 0x12 R_LARCH_SUB32 .L2 0x0 +# RELAX-NEXT: 0x16 R_LARCH_ADD64 .L3 0x0 +# RELAX-NEXT: 0x16 R_LARCH_SUB64 .L2 0x0 +# RELAX-NEXT: } +# RELAX-NEXT: ] + +# RELAX: Hex dump of section '.data': +# RELAX-NEXT: 0x00000000 04040004 00000004 00000000 00000000 +# RELAX-NEXT: 0x00000010 00000000 00000000 00000000 00000808 +# RELAX-NEXT: 0x00000020 00080000 00080000 00000000 00 + +.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 +## With relaxation, emit relocs because of the .align making the diff variable. +## TODO Handle alignment directive. Why they emit relocs now? They returns +## without folding symbols offset in AttemptToFoldSymbolOffsetDifference(). +.byte .L3 - .L2 +.short .L3 - .L2 +.word .L3 - .L2 +.dword .L3 - .L2 +## TODO +## With relaxation, emit relocs because la.pcrel is a linker-relaxable inst. +.byte .L4 - .L3 +.short .L4 - .L3 +.word .L4 - .L3 +.dword .L4 - .L3 -- Gitee From 504cf5e0d0a5b953430d0e1a634133c4915fdf10 Mon Sep 17 00:00:00 2001 From: Jinyang He Date: Wed, 27 Dec 2023 08:51:48 +0800 Subject: [PATCH 04/14] [LoongArch] Emit R_LARCH_RELAX when expanding some LoadAddress (#72961) Emit relax relocs when expand non-large la.pcrel and non-large la.got on llvm-mc stage, which like what does on GAS. 1, la.pcrel -> PCALA_HI20 + RELAX + PCALA_LO12 + RELAX 2, la.got -> GOT_PC_HI20 + RELAX + GOT_PC_LO12 + RELAX (cherry picked from commit b3ef8dce9811b2725639b0d4fac3f85c7e112817) --- .../AsmParser/LoongArchAsmParser.cpp | 12 +-- .../MCTargetDesc/LoongArchMCCodeEmitter.cpp | 13 +++ .../MCTargetDesc/LoongArchMCExpr.cpp | 7 +- .../LoongArch/MCTargetDesc/LoongArchMCExpr.h | 8 +- llvm/test/MC/LoongArch/Macros/macros-la.s | 84 ++++++++++++++++--- llvm/test/MC/LoongArch/Misc/subsection.s | 2 +- .../MC/LoongArch/Relocations/relax-addsub.s | 16 +++- 7 files changed, 115 insertions(+), 27 deletions(-) diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp index 94d530306536..a132e645c864 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/MCTargetDesc/LoongArchMCCodeEmitter.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp index 03fb9e008ae9..08c0820cb862 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 993111552a31..82c992b1cc8c 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 0945cf82db86..93251f824103 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/test/MC/LoongArch/Macros/macros-la.s b/llvm/test/MC/LoongArch/Macros/macros-la.s index 924e4326b8e5..1a1d12d7d7df 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/subsection.s b/llvm/test/MC/LoongArch/Misc/subsection.s index 0bd22b474536..566a2408d691 100644 --- a/llvm/test/MC/LoongArch/Misc/subsection.s +++ b/llvm/test/MC/LoongArch/Misc/subsection.s @@ -1,5 +1,5 @@ # 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: -## TODO: 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: +# 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 diff --git a/llvm/test/MC/LoongArch/Relocations/relax-addsub.s b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s index 532eb4e0561a..c4454f5bb98d 100644 --- a/llvm/test/MC/LoongArch/Relocations/relax-addsub.s +++ b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s @@ -18,7 +18,9 @@ # RELAX: Relocations [ # RELAX-NEXT: Section ({{.*}}) .rela.text { # 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: 0xF R_LARCH_ADD8 .L3 0x0 @@ -29,13 +31,21 @@ # RELAX-NEXT: 0x12 R_LARCH_SUB32 .L2 0x0 # RELAX-NEXT: 0x16 R_LARCH_ADD64 .L3 0x0 # RELAX-NEXT: 0x16 R_LARCH_SUB64 .L2 0x0 +# RELAX-NEXT: 0x1E R_LARCH_ADD8 .L4 0x0 +# RELAX-NEXT: 0x1E R_LARCH_SUB8 .L3 0x0 +# RELAX-NEXT: 0x1F R_LARCH_ADD16 .L4 0x0 +# RELAX-NEXT: 0x1F R_LARCH_SUB16 .L3 0x0 +# RELAX-NEXT: 0x21 R_LARCH_ADD32 .L4 0x0 +# RELAX-NEXT: 0x21 R_LARCH_SUB32 .L3 0x0 +# RELAX-NEXT: 0x25 R_LARCH_ADD64 .L4 0x0 +# RELAX-NEXT: 0x25 R_LARCH_SUB64 .L3 0x0 # RELAX-NEXT: } # RELAX-NEXT: ] # RELAX: Hex dump of section '.data': # RELAX-NEXT: 0x00000000 04040004 00000004 00000000 00000000 -# RELAX-NEXT: 0x00000010 00000000 00000000 00000000 00000808 -# RELAX-NEXT: 0x00000020 00080000 00080000 00000000 00 +# RELAX-NEXT: 0x00000010 00000000 00000000 00000000 00000000 +# RELAX-NEXT: 0x00000020 00000000 00000000 00000000 00 .text .L1: @@ -60,8 +70,6 @@ .short .L3 - .L2 .word .L3 - .L2 .dword .L3 - .L2 -## TODO -## With relaxation, emit relocs because la.pcrel is a linker-relaxable inst. .byte .L4 - .L3 .short .L4 - .L3 .word .L4 - .L3 -- Gitee From fcec120ea922a472454584809242ceddf566f3aa Mon Sep 17 00:00:00 2001 From: Jinyang He Date: Thu, 14 Mar 2024 12:10:50 +0800 Subject: [PATCH 05/14] [MC][LoongArch] Add AlignFragment size if layout is available and not need insert nops (#76552) Due to delayed decision for ADD/SUB relocations, RISCV and LoongArch may go slow fragment walk path with available layout. When RISCV (or LoongArch in the future) don't need insert nops, that means relax is disabled. With available layout and not needing insert nops, the size of AlignFragment should be a constant. So we can add it to Displacement for folding A-B. (cherry picked from commit 0731567a31e4ade97c27801045156a88c4589704) --- llvm/lib/MC/MCExpr.cpp | 6 +++++ llvm/test/MC/LoongArch/Misc/cfi-advance.s | 27 +++++++++++++++++++ .../MC/LoongArch/Relocations/relax-addsub.s | 17 +++--------- 3 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 llvm/test/MC/LoongArch/Misc/cfi-advance.s diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp index 5a6596f93824..a561fed11179 100644 --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -707,8 +707,14 @@ static void AttemptToFoldSymbolOffsetDifference( } int64_t Num; + unsigned Count; if (DF) { Displacement += DF->getContents().size(); + } else if (auto *AF = dyn_cast(FI); + AF && Layout && + !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/test/MC/LoongArch/Misc/cfi-advance.s b/llvm/test/MC/LoongArch/Misc/cfi-advance.s new file mode 100644 index 000000000000..662c43e6bcea --- /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/Relocations/relax-addsub.s b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s index c4454f5bb98d..14922657ae89 100644 --- a/llvm/test/MC/LoongArch/Relocations/relax-addsub.s +++ b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s @@ -23,14 +23,6 @@ # RELAX-NEXT: 0x14 R_LARCH_RELAX - 0x0 # RELAX-NEXT: } # RELAX-NEXT: Section ({{.*}}) .rela.data { -# RELAX-NEXT: 0xF R_LARCH_ADD8 .L3 0x0 -# RELAX-NEXT: 0xF R_LARCH_SUB8 .L2 0x0 -# RELAX-NEXT: 0x10 R_LARCH_ADD16 .L3 0x0 -# RELAX-NEXT: 0x10 R_LARCH_SUB16 .L2 0x0 -# RELAX-NEXT: 0x12 R_LARCH_ADD32 .L3 0x0 -# RELAX-NEXT: 0x12 R_LARCH_SUB32 .L2 0x0 -# RELAX-NEXT: 0x16 R_LARCH_ADD64 .L3 0x0 -# RELAX-NEXT: 0x16 R_LARCH_SUB64 .L2 0x0 # RELAX-NEXT: 0x1E R_LARCH_ADD8 .L4 0x0 # RELAX-NEXT: 0x1E R_LARCH_SUB8 .L3 0x0 # RELAX-NEXT: 0x1F R_LARCH_ADD16 .L4 0x0 @@ -43,8 +35,8 @@ # RELAX-NEXT: ] # RELAX: Hex dump of section '.data': -# RELAX-NEXT: 0x00000000 04040004 00000004 00000000 00000000 -# RELAX-NEXT: 0x00000010 00000000 00000000 00000000 00000000 +# RELAX-NEXT: 0x00000000 04040004 00000004 00000000 0000000c +# RELAX-NEXT: 0x00000010 0c000c00 00000c00 00000000 00000000 # RELAX-NEXT: 0x00000020 00000000 00000000 00000000 00 .text @@ -63,13 +55,12 @@ .short .L2 - .L1 .word .L2 - .L1 .dword .L2 - .L1 -## With relaxation, emit relocs because of the .align making the diff variable. -## TODO Handle alignment directive. Why they emit relocs now? They returns -## without folding symbols offset in AttemptToFoldSymbolOffsetDifference(). +## TODO Handle alignment directive. .byte .L3 - .L2 .short .L3 - .L2 .word .L3 - .L2 .dword .L3 - .L2 +## With relaxation, emit relocs because the la.pcrel makes the diff variable. .byte .L4 - .L3 .short .L4 - .L3 .word .L4 - .L3 -- Gitee From 6576984cc3a8509c8cccea7de5881f5a2b3c47cc Mon Sep 17 00:00:00 2001 From: zhanglimin Date: Fri, 15 Mar 2024 14:39:48 +0800 Subject: [PATCH 06/14] [Backport][LoongArch][RISCV] Support R_LARCH_{ADD,SUB}_ULEB128/R_RISCV_{SET,SUB}_ULEB128 for .uleb128 directives This patch is originally from three upstream commits: 1, R_LARCH_{ADD,SUB}_ULEB128 are originally landed from b57159cb(#76433). 2, R_RISCV_{SET,SUB}_ULEB128 are originally supported from 1df5ea29. Among it, we change the default behaviour of `-riscv-uleb128-reloc` to not produce uleb128 reloc, in order to avoid any other side-effects due to the updated implementation of `MCAssembler::relaxLEB()` function. And at the same time, we ensure that this patch can't introduce new default traits (such as the generation for uleb128 reloc) on RISCV in this version. 3, Fix invalid-sleb.s in original commit d7398a35. --- .../llvm/BinaryFormat/ELFRelocs/RISCV.def | 2 + llvm/include/llvm/MC/MCAsmBackend.h | 8 +++ llvm/include/llvm/MC/MCFixup.h | 1 + llvm/include/llvm/MC/MCFragment.h | 9 ++- llvm/lib/MC/MCAsmBackend.cpp | 1 + llvm/lib/MC/MCAssembler.cpp | 39 ++++++++-- .../MCTargetDesc/LoongArchAsmBackend.cpp | 69 ++++++++++++++---- .../MCTargetDesc/LoongArchAsmBackend.h | 3 + .../RISCV/MCTargetDesc/RISCVAsmBackend.cpp | 27 +++++++ .../RISCV/MCTargetDesc/RISCVAsmBackend.h | 2 + llvm/test/MC/ELF/RISCV/gen-dwarf.s | 5 +- llvm/test/MC/LoongArch/Relocations/leb128.s | 72 +++++++++++++++++++ .../MC/LoongArch/Relocations/relax-addsub.s | 57 +++++++++++---- llvm/test/MC/X86/invalid-sleb.s | 5 -- 14 files changed, 252 insertions(+), 48 deletions(-) create mode 100644 llvm/test/MC/LoongArch/Relocations/leb128.s delete mode 100644 llvm/test/MC/X86/invalid-sleb.s diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def index 9a126df01531..c7fd6490041c 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 5e08fb41679b..968a767b17f8 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 069ca058310f..7f48a90cb1ec 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 7be4792a4521..e965732010fe 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 64bbc63719c7..2eef7d363fe7 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 55ed1a285cd7..86c798ec9e27 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/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp index 1ed047a8e632..9227d4d6afed 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp @@ -92,6 +92,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 +130,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 +154,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); @@ -173,6 +187,7 @@ bool LoongArchAsmBackend::shouldForceRelocation(const MCAssembler &Asm, case FK_Data_2: case FK_Data_4: case FK_Data_8: + case FK_Data_leb128: return !Target.isAbsolute(); } } @@ -202,9 +217,24 @@ getRelocPairForSize(unsigned Size) { 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::writeNopData(raw_ostream &OS, uint64_t Count, const MCSubtargetInfo *STI) const { // We mostly follow binutils' convention here: align to 4-byte boundary with a @@ -226,21 +256,27 @@ bool LoongArchAsmBackend::handleAddSubRelocations(const MCAsmLayout &Layout, uint64_t &FixedValue) const { std::pair FK; uint64_t FixedValueA, FixedValueB; - const MCSection &SecA = Target.getSymA()->getSymbol().getSection(); - const MCSection &SecB = Target.getSymB()->getSymbol().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 - // calculated out in evaluateFixup, return true and avoid record relocations. - if (!STI.hasFeature(LoongArch::FeatureRelax)) - return true; + 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: @@ -255,6 +291,9 @@ bool LoongArchAsmBackend::handleAddSubRelocations(const MCAsmLayout &Layout, case llvm::FK_Data_8: FK = getRelocPairForSize(64); break; + case llvm::FK_Data_leb128: + FK = getRelocPairForSize(128); + break; default: llvm_unreachable("unsupported fixup size"); } diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h index 20f25b5cf53b..49801e4fd81a 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h @@ -65,6 +65,9 @@ public: void relaxInstruction(MCInst &Inst, const MCSubtargetInfo &STI) 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/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp index 1b890fbe041a..5c651aa93225 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 0ea1f32e8296..edefb171bcdc 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/MC/ELF/RISCV/gen-dwarf.s b/llvm/test/MC/ELF/RISCV/gen-dwarf.s index 2235559d5f35..2a7dc777e70c 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/LoongArch/Relocations/leb128.s b/llvm/test/MC/LoongArch/Relocations/leb128.s new file mode 100644 index 000000000000..7a96ec551b76 --- /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 index 14922657ae89..cd01332afd0b 100644 --- a/llvm/test/MC/LoongArch/Relocations/relax-addsub.s +++ b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s @@ -8,12 +8,23 @@ # 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 0000000c -# NORELAX-NEXT: 0x00000010 0c000c00 00000c00 00000000 00000808 -# NORELAX-NEXT: 0x00000020 00080000 00080000 00000000 00 +# 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 { @@ -23,21 +34,32 @@ # RELAX-NEXT: 0x14 R_LARCH_RELAX - 0x0 # RELAX-NEXT: } # RELAX-NEXT: Section ({{.*}}) .rela.data { -# RELAX-NEXT: 0x1E R_LARCH_ADD8 .L4 0x0 -# RELAX-NEXT: 0x1E R_LARCH_SUB8 .L3 0x0 -# RELAX-NEXT: 0x1F R_LARCH_ADD16 .L4 0x0 -# RELAX-NEXT: 0x1F R_LARCH_SUB16 .L3 0x0 -# RELAX-NEXT: 0x21 R_LARCH_ADD32 .L4 0x0 -# RELAX-NEXT: 0x21 R_LARCH_SUB32 .L3 0x0 -# RELAX-NEXT: 0x25 R_LARCH_ADD64 .L4 0x0 -# RELAX-NEXT: 0x25 R_LARCH_SUB64 .L3 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 0000000c -# RELAX-NEXT: 0x00000010 0c000c00 00000c00 00000000 00000000 -# RELAX-NEXT: 0x00000020 00000000 00000000 00000000 00 +# RELAX-NEXT: 0x00000000 04040004 00000004 00000000 00000004 +# RELAX-NEXT: 0x00000010 0c0c000c 0000000c 00000000 0000000c +# RELAX-NEXT: 0x00000020 00000000 00000000 00000000 00000000 +# RELAX-NEXT: 0x00000030 00000000 00000000 00000000 000000 .text .L1: @@ -55,13 +77,20 @@ .short .L2 - .L1 .word .L2 - .L1 .dword .L2 - .L1 +.uleb128 .L2 - .L1 ## TODO Handle alignment directive. .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/X86/invalid-sleb.s b/llvm/test/MC/X86/invalid-sleb.s deleted file mode 100644 index 7d7df351ce4e..000000000000 --- 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 -- Gitee From f4a5f7f21fdabf2efba4a6c6364364e010ff2fcf Mon Sep 17 00:00:00 2001 From: Jinyang He Date: Tue, 16 Jan 2024 13:20:13 +0800 Subject: [PATCH 07/14] [LoongArch] Add relaxDwarfLineAddr and relaxDwarfCFA to handle the mutable label diff in dwarfinfo (#77728) When linker-relaxation is enabled, part of the label diff in dwarfinfo cannot be computed before static link. Refer to RISCV, we add the relaxDwarfLineAddr and relaxDwarfCFA to add relocations for these label diffs. Calculate whether the label diff is mutable. For immutable label diff, return false and do the other works by its parent function. (cherry picked from commit ed7f4edc19ada006789318a0929b57d1b5a761bd) --- llvm/lib/Object/RelocationResolver.cpp | 12 +- .../MCTargetDesc/LoongArchAsmBackend.cpp | 129 ++++++++++++++++++ .../MCTargetDesc/LoongArchAsmBackend.h | 5 + .../LoongArch/dwarf-loongarch-relocs.ll | 128 +++++++++++++++++ llvm/test/DebugInfo/LoongArch/lit.local.cfg | 2 + 5 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 llvm/test/DebugInfo/LoongArch/dwarf-loongarch-relocs.ll create mode 100644 llvm/test/DebugInfo/LoongArch/lit.local.cfg diff --git a/llvm/lib/Object/RelocationResolver.cpp b/llvm/lib/Object/RelocationResolver.cpp index 03ac59289528..0e5036d7dfcc 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/MCTargetDesc/LoongArchAsmBackend.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp index 9227d4d6afed..8d82327b2e2b 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp @@ -12,6 +12,7 @@ #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" @@ -19,6 +20,7 @@ #include "llvm/MC/MCValue.h" #include "llvm/Support/Endian.h" #include "llvm/Support/EndianStream.h" +#include "llvm/Support/LEB128.h" #define DEBUG_TYPE "loongarch-asmbackend" @@ -235,6 +237,133 @@ std::pair LoongArchAsmBackend::relaxLEB128(MCLEBFragment &LF, 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 diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h index 49801e4fd81a..657f5ca5e731 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h @@ -68,6 +68,11 @@ public: 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; 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 000000000000..e03b4c1d34de --- /dev/null +++ b/llvm/test/DebugInfo/LoongArch/dwarf-loongarch-relocs.ll @@ -0,0 +1,128 @@ +; 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-prefix=SOURCE %s +; RUN: llvm-dwarfdump --debug-info --debug-line %t.o | FileCheck --check-prefix=DWARF %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-prefix=SOURCE %s +; RUN: llvm-dwarfdump --debug-info --debug-line %t.r.o | FileCheck --check-prefix=DWARF %s + +; RELOCS-BOTH: Relocations [ +; RELOCS-BOTH-NEXT: Section ({{.*}}) .rela.text { +; RELOCS-BOTH-NEXT: 0x14 R_LARCH_PCALA_HI20 sym 0x0 +; RELOCS-ENRL-NEXT: 0x14 R_LARCH_RELAX - 0x0 +; RELOCS-BOTH-NEXT: 0x18 R_LARCH_PCALA_LO12 sym 0x0 +; RELOCS-ENRL-NEXT: 0x18 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: 0000000000000000 : +; 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-NEXT: 0x0000000000000000 2 0 0 0 0 0 is_stmt +; DWARF-NEXT: 0x0000000000000010 3 3 0 0 0 0 is_stmt prologue_end +; DWARF-NEXT: 0x0000000000000020 10 3 0 0 0 0 is_stmt +; DWARF-NEXT: 0x000000000000002c 10 3 0 0 0 0 epilogue_begin +; DWARF-NEXT: 0x0000000000000034 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 000000000000..77becb8eee90 --- /dev/null +++ b/llvm/test/DebugInfo/LoongArch/lit.local.cfg @@ -0,0 +1,2 @@ +if "LoongArch" not in config.root.targets: + config.unsupported = True -- Gitee From 6b165c494a2de00aa5ce070e4e32bbad70f9bd06 Mon Sep 17 00:00:00 2001 From: Jinyang He Date: Wed, 24 Jan 2024 09:17:49 +0800 Subject: [PATCH 08/14] [LoongArch] Insert nops and emit align reloc when handle alignment directive (#72962) Refer to RISCV, we will fix up the alignment if linker relaxation changes code size and breaks alignment. Insert enough Nops and emit R_LARCH_ALIGN relocation type so that linker could satisfy the alignment by removing Nops. It does so only in sections with the SHF_EXECINSTR flag. In LoongArch psABI v2.30, 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. (cherry picked from commit c51ab483e6c2d991a01179584705b83fbea1940d) --- llvm/lib/MC/MCExpr.cpp | 2 +- .../MCTargetDesc/LoongArchAsmBackend.cpp | 67 ++++++++++++++++ .../MCTargetDesc/LoongArchAsmBackend.h | 15 ++++ .../MCTargetDesc/LoongArchFixupKinds.h | 4 +- .../Relocations/align-non-executable.s | 27 +++++++ .../MC/LoongArch/Relocations/relax-addsub.s | 15 +++- .../MC/LoongArch/Relocations/relax-align.s | 79 +++++++++++++++++++ 7 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 llvm/test/MC/LoongArch/Relocations/align-non-executable.s create mode 100644 llvm/test/MC/LoongArch/Relocations/relax-align.s diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp index a561fed11179..79808a58d81c 100644 --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -711,7 +711,7 @@ static void AttemptToFoldSymbolOffsetDifference( if (DF) { Displacement += DF->getContents().size(); } else if (auto *AF = dyn_cast(FI); - AF && Layout && + AF && Layout && AF->hasEmitNops() && !Asm->getBackend().shouldInsertExtraNopBytesForCodeAlign( *AF, Count)) { Displacement += Asm->computeFragmentSize(*Layout, *AF); diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp index 8d82327b2e2b..8c482356402f 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp @@ -17,10 +17,13 @@ #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" @@ -177,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) { diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h index 657f5ca5e731..71bbd003888a 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,6 +29,7 @@ class LoongArchAsmBackend : public MCAsmBackend { uint8_t OSABI; bool Is64Bit; const MCTargetOptions &TargetOptions; + DenseMap SecToAlignSym; public: LoongArchAsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI, bool Is64Bit, @@ -45,6 +48,15 @@ public: 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; @@ -79,6 +91,9 @@ public: 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/LoongArchFixupKinds.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h index 178fa6e5262b..78414408f21f 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h @@ -108,7 +108,9 @@ enum Fixups { // 20-bit fixup corresponding to %gd_hi20(foo) for instruction lu12i.w. 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 + 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/test/MC/LoongArch/Relocations/align-non-executable.s b/llvm/test/MC/LoongArch/Relocations/align-non-executable.s new file mode 100644 index 000000000000..47834acd9521 --- /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/relax-addsub.s b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s index cd01332afd0b..18e0ede5e293 100644 --- a/llvm/test/MC/LoongArch/Relocations/relax-addsub.s +++ b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s @@ -28,12 +28,23 @@ # 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 @@ -57,7 +68,7 @@ # RELAX: Hex dump of section '.data': # RELAX-NEXT: 0x00000000 04040004 00000004 00000000 00000004 -# RELAX-NEXT: 0x00000010 0c0c000c 0000000c 00000000 0000000c +# RELAX-NEXT: 0x00000010 00000000 00000000 00000000 00000000 # RELAX-NEXT: 0x00000020 00000000 00000000 00000000 00000000 # RELAX-NEXT: 0x00000030 00000000 00000000 00000000 000000 @@ -78,7 +89,7 @@ .word .L2 - .L1 .dword .L2 - .L1 .uleb128 .L2 - .L1 -## TODO Handle alignment directive. +## With relaxation, emit relocs because the .align makes the diff variable. .byte .L3 - .L2 .short .L3 - .L2 .word .L3 - .L2 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 000000000000..294fd9fb916c --- /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: ] -- Gitee From 364be3e5e68f90e77cbd9df20b893cf93fcc0c8f Mon Sep 17 00:00:00 2001 From: Job Noorman Date: Sat, 9 Sep 2023 10:24:16 +0200 Subject: [PATCH 09/14] [ELF][RISCV] Implement --emit-relocs with relaxation Linker relaxation may change relocations (offsets and types). However, when --emit-relocs is used, relocations are simply copied from the input section causing a mismatch with the corresponding (relaxed) code section. This patch fixes this as follows: for non-relocatable RISC-V binaries, `InputSection::copyRelocations` reads relocations from the relocated section's `relocations` array (since this gets updated by the relaxation code). For all other cases, relocations are read from the input section directly as before. In order to reuse as much code as possible, and to keep the diff small, the original `InputSection::copyRelocations` is changed to accept the relocations as a range of `Relocation` objects. This means that, in the general case when reading from the input section, raw relocations need to be converted to `Relocation`s first, which introduces quite a bit of boiler plate. It also means there's a slight code size increase due to the extra instantiations of `copyRelocations` (for both range types). Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D159082 (cherry picked from commit 649cac3b627fa3d466b8807536c8be970cc8c32f) --- lld/ELF/InputSection.cpp | 56 ++++++++++++++++----- lld/ELF/InputSection.h | 6 ++- lld/test/ELF/riscv-relax-emit-relocs.s | 69 ++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 lld/test/ELF/riscv-relax-emit-relocs.s diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 2edaa2b40493..1aff6b968d86 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -349,29 +349,61 @@ InputSectionBase *InputSection::getRelocatedSection() const { return sections[info]; } +template +void InputSection::copyRelocations(uint8_t *buf) { + if (config->relax && !config->relocatable && config->emachine == EM_RISCV) { + // On 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 +440,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 +464,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 +1138,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 15122d6abd6b..2b91711abba3 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -396,8 +396,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/test/ELF/riscv-relax-emit-relocs.s b/lld/test/ELF/riscv-relax-emit-relocs.s new file mode 100644 index 000000000000..ebd69b742d4f --- /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 -- Gitee From 1240cce80edf31259a37474583fb0868a8d4c5a8 Mon Sep 17 00:00:00 2001 From: Jinyang He Date: Tue, 6 Feb 2024 09:09:13 +0800 Subject: [PATCH 10/14] [lld][ELF] Support relax R_LARCH_ALIGN (#78692) Refer to commit 6611d58f5bbc ("Relax R_RISCV_ALIGN"), we can relax R_LARCH_ALIGN by same way. Reuse `SymbolAnchor`, `RISCVRelaxAux` and `initSymbolAnchors` to simplify codes. As `riscvFinalizeRelax` is an arch-specific function, put it override on `TargetInfo::finalizeRelax`, so that LoongArch can override it, too. The flow of relax R_LARCH_ALIGN is almost consistent with RISCV. The difference is that LoongArch only has 4-bytes NOP and all executable insn is 4-bytes aligned. So LoongArch not need rewrite NOP sequence. Alignment maxBytesEmit parameter is supported in psABI v2.30. (cherry picked from commit 06a728f3feab876f9195738b5774e82dadc0f3a7) (cherry picked from commit 60a8ec3a35c722a9eb8298c215321b89d0faf5b5) --- lld/ELF/Arch/LoongArch.cpp | 156 ++++++++++++++++++++- lld/ELF/Arch/RISCV.cpp | 28 +--- lld/ELF/InputSection.cpp | 5 +- lld/ELF/InputSection.h | 24 +++- lld/ELF/Target.h | 3 + lld/ELF/Writer.cpp | 4 +- lld/test/ELF/loongarch-relax-align.s | 126 +++++++++++++++++ lld/test/ELF/loongarch-relax-emit-relocs.s | 49 +++++++ 8 files changed, 362 insertions(+), 33 deletions(-) create mode 100644 lld/test/ELF/loongarch-relax-align.s create mode 100644 lld/test/ELF/loongarch-relax-emit-relocs.s diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp index d3a538577a59..3f57a76873f9 100644 --- a/lld/ELF/Arch/LoongArch.cpp +++ b/lld/ELF/Arch/LoongArch.cpp @@ -36,6 +36,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 @@ -521,8 +523,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: // @@ -696,6 +699,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 d0d75118e30d..06120cabc132 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 1aff6b968d86..b178d82407e3 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -351,8 +351,9 @@ InputSectionBase *InputSection::getRelocatedSection() const { template void InputSection::copyRelocations(uint8_t *buf) { - if (config->relax && !config->relocatable && config->emachine == EM_RISCV) { - // On RISC-V, relaxation might change relocations: copy from + 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(), diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index 2b91711abba3..842bc369909d 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; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index 47dbe6b4d1c6..bf831afa1793 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 368c9aabceae..dd37bbbf76c1 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-relax-align.s b/lld/test/ELF/loongarch-relax-align.s new file mode 100644 index 000000000000..ab61e15d5cac --- /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 000000000000..581fce8c95ca --- /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 -- Gitee From 21aecdf7e3991eb94f2c80bc14ab528a33d86b4f Mon Sep 17 00:00:00 2001 From: Weining Lu Date: Wed, 24 Jan 2024 11:03:14 +0800 Subject: [PATCH 11/14] [test] Update dwarf-loongarch-relocs.ll Address buildbot faiures: http://45.33.8.238/macm1/77360/step_11.txt http://45.33.8.238/linux/128902/step_12.txt (cherry picked from commit baba7e4175b6ca21e83b1cf8229f29dbba02e979) (cherry picked from commit c9e73cdd9a17f15ede120ea57657553f9e105eab) --- .../LoongArch/dwarf-loongarch-relocs.ll | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/llvm/test/DebugInfo/LoongArch/dwarf-loongarch-relocs.ll b/llvm/test/DebugInfo/LoongArch/dwarf-loongarch-relocs.ll index e03b4c1d34de..07443a62b933 100644 --- a/llvm/test/DebugInfo/LoongArch/dwarf-loongarch-relocs.ll +++ b/llvm/test/DebugInfo/LoongArch/dwarf-loongarch-relocs.ll @@ -1,19 +1,22 @@ ; 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-prefix=SOURCE %s -; RUN: llvm-dwarfdump --debug-info --debug-line %t.o | FileCheck --check-prefix=DWARF %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-prefix=SOURCE %s -; RUN: llvm-dwarfdump --debug-info --debug-line %t.r.o | FileCheck --check-prefix=DWARF %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-BOTH-NEXT: 0x14 R_LARCH_PCALA_HI20 sym 0x0 -; RELOCS-ENRL-NEXT: 0x14 R_LARCH_RELAX - 0x0 -; RELOCS-BOTH-NEXT: 0x18 R_LARCH_PCALA_LO12 sym 0x0 -; RELOCS-ENRL-NEXT: 0x18 R_LARCH_RELAX - 0x0 +; 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 @@ -36,7 +39,8 @@ ; RELOCS-BOTH-NEXT: } ; RELOCS-BOTH-NEXT: ] -; SOURCE: 0000000000000000 : +; SOURCE-NORL: 0000000000000000 : +; SOURCE-ENRL: 000000000000001c : ; SOURCE: ; { ; SOURCE: ; asm volatile( ; SOURCE: ; return 0; @@ -87,11 +91,16 @@ ; DWARF-EMPTY: ; DWARF-NEXT: Address Line Column File ISA Discriminator OpIndex Flags ; DWARF-NEXT: ------------------ ------ ------ ------ --- ------------- ------- ------------- -; DWARF-NEXT: 0x0000000000000000 2 0 0 0 0 0 is_stmt -; DWARF-NEXT: 0x0000000000000010 3 3 0 0 0 0 is_stmt prologue_end -; DWARF-NEXT: 0x0000000000000020 10 3 0 0 0 0 is_stmt -; DWARF-NEXT: 0x000000000000002c 10 3 0 0 0 0 epilogue_begin -; DWARF-NEXT: 0x0000000000000034 10 3 0 0 0 0 end_sequence +; 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" -- Gitee From ad7354e3e32789e612619666a549c9bb837958fc Mon Sep 17 00:00:00 2001 From: Jinyang He Date: Tue, 5 Mar 2024 15:50:14 +0800 Subject: [PATCH 12/14] [lld][LoongArch] Support the R_LARCH_{ADD,SUB}_ULEB128 relocation types (#81133) For a label difference like `.uleb128 A-B`, MC generates a pair of R_LARCH_{ADD,SUB}_ULEB128 if A-B cannot be folded as a constant. GNU assembler generates a pair of relocations in more cases (when A or B is in a code section with linker relaxation). It is similar to RISCV. R_LARCH_{ADD,SUB}_ULEB128 relocations are created by Clang and GCC in `.gcc_except_table` and other debug sections with linker relaxation enabled. On LoongArch, first read the buf and count the available space. Then add or sub the value. Finally truncate the expected value and fill it into the available space. (cherry picked from commit eaa9ef678c63bf392ec2d5b736605db7ea7e7338) --- lld/ELF/Arch/LoongArch.cpp | 19 +++++ lld/test/ELF/loongarch-reloc-leb128.s | 102 ++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 lld/test/ELF/loongarch-reloc-leb128.s diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp index 3f57a76873f9..160fab4aeba9 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; @@ -210,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 @@ -451,11 +462,13 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s, 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; @@ -670,6 +683,9 @@ 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; @@ -685,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: diff --git a/lld/test/ELF/loongarch-reloc-leb128.s b/lld/test/ELF/loongarch-reloc-leb128.s new file mode 100644 index 000000000000..7740ca797fca --- /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 -- Gitee From 89a573fb75fbc7a9a5855a099f93af316c4b2470 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 9 Aug 2023 21:42:18 -0700 Subject: [PATCH 13/14] [MC][test] Change ELF/uleb-ehtable.s Mach-O to use private symbols in .uleb128 for label differences On Mach-O, `.uleb128 A-B` where A and B are separated by a non-private symbol is invalid (see D153167). (cherry picked from commit 0a89bda4a8b756a00985e0965f7686b5ceb43295) --- llvm/test/MC/ELF/uleb-ehtable.s | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/llvm/test/MC/ELF/uleb-ehtable.s b/llvm/test/MC/ELF/uleb-ehtable.s index ca3f9e97bffc..6407223f36e7 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 -- Gitee From 2c73814ab6128156cdc5a5c6ef0301a0e3ca453b Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 16 Aug 2023 23:11:59 -0700 Subject: [PATCH 14/14] [Mips][MC] AttemptToFoldSymbolOffsetDifference: revert isMicroMips special case D52985/D57677 added a .gcc_except_table workaround, but the new behavior doesn't match GNU assembler. ``` void foo(); int bar() { foo(); try { throw 1; } catch (int) { return 1; } return 0; } clang --target=mipsel-linux-gnu -mmicromips -S a.cc mipsel-linux-gnu-gcc -mmicromips -c a.s -o gnu.o .uleb128 ($cst_end0)-($cst_begin0) // bit 0 is not forced to 1 .uleb128 ($func_begin0)-($func_begin0) // bit 0 is not forced to 1 ``` I have inspected `.gcc_except_table` output by `mipsel-linux-gnu-gcc -mmicromips -c a.cc`. The `.uleb128` values are not forced to set the least significant bit. In addition, D57677's adjustment (even->odd) to CodeGen/Mips/micromips-b-range.ll is wrong. PC-relative `.long func - .` values will differ from GNU assembler as well. The original intention of D52985 seems unclear to me. I think whatever goal it wants to achieve should be moved to an upper layer. This isMicroMips special case has caused problems to fix MCAssembler::relaxLEB to use evaluateAsAbsolute instead of evaluateKnownAbsolute, which is needed to proper support R_RISCV_SET_ULEB128/R_RISCV_SUB_ULEB128. Differential Revision: https://reviews.llvm.org/D157655 (cherry picked from commit 4c89277095ee7cda3d20e0f5f18b384212069778) --- llvm/lib/MC/MCExpr.cpp | 5 ----- llvm/test/CodeGen/Mips/micromips-b-range.ll | 8 ++++---- llvm/test/CodeGen/Mips/micromips-gcc-except-table.ll | 2 +- llvm/test/DebugInfo/Mips/eh_frame.ll | 4 ++-- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp index 79808a58d81c..c9ff1865cf91 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; diff --git a/llvm/test/CodeGen/Mips/micromips-b-range.ll b/llvm/test/CodeGen/Mips/micromips-b-range.ll index 064afff3da0e..81d1c04208cc 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 2b63aff01574..20d64fc216b7 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/Mips/eh_frame.ll b/llvm/test/DebugInfo/Mips/eh_frame.ll index 506e5b87892b..60d4dc76777e 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 -- Gitee