diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp index 73e6569f96e463..061f2ad13ffa78 100644 --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -632,7 +632,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 14bcef7c7d265c..6d8ef1bf96cbab 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 d1fbf788e8a8d8..fef0e84600a74c 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(llvm::endianness::little), STI(STI), OSABI(OSABI), - Is64Bit(Is64Bit), TargetOptions(Options) {} + : MCAsmBackend(llvm::endianness::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 ba2d6718cdf9a2..178fa6e5262be3 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 00000000000000..0bd22b474536c0 --- /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 00000000000000..532eb4e0561ac7 --- /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