diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp index 3280c34cb6ed0..dbf024eadf100 100644 --- a/lld/ELF/Arch/LoongArch.cpp +++ b/lld/ELF/Arch/LoongArch.cpp @@ -53,6 +53,7 @@ enum Op { ADDI_W = 0x02800000, ADDI_D = 0x02c00000, ANDI = 0x03400000, + PCADDI = 0x18000000, PCADDU12I = 0x1c000000, LD_W = 0x28800000, LD_D = 0x28c00000, @@ -131,6 +132,10 @@ static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) { return begin == 63 ? v >> end : (v & ((1ULL << (begin + 1)) - 1)) >> end; } +static uint32_t getD5(uint64_t v) { return extractBits(v, 4, 0); } + +static uint32_t getJ5(uint64_t v) { return extractBits(v, 9, 5); } + static uint32_t setD5k16(uint32_t insn, uint32_t imm) { uint32_t immLo = extractBits(imm, 15, 0); uint32_t immHi = extractBits(imm, 20, 16); @@ -743,6 +748,88 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel, } } +static bool relaxable(ArrayRef relocs, size_t i) { + return i + 1 < relocs.size() && relocs[i + 1].type == R_LARCH_RELAX; +} + +static bool isPairRelaxable(ArrayRef relocs, size_t i) { + return relaxable(relocs, i) && relaxable(relocs, i + 2) && + relocs[i].offset + 4 == relocs[i + 2].offset; +} + +// Relax code sequence. +// From: +// pcalau12i $a0, %pc_hi20(sym) +// addi.w/d $a0, $a0, %pc_lo12(sym) +// To: +// pcaddi $a0, %pc_lo12(sym) +// +// From: +// pcalau12i $a0, %got_pc_hi20(sym_got) +// ld.w/d $a0, $a0, %got_pc_lo12(sym_got) +// To: +// pcaddi $a0, %got_pc_hi20(sym_got) +static void relaxPCHi20Lo12(Ctx &ctx, const InputSection &sec, size_t i, + uint64_t loc, Relocation &rHi20, Relocation &rLo12, + uint32_t &remove) { + // check if the relocations are relaxable sequences. + if (!((rHi20.type == R_LARCH_PCALA_HI20 && + rLo12.type == R_LARCH_PCALA_LO12) || + (rHi20.type == R_LARCH_GOT_PC_HI20 && + rLo12.type == R_LARCH_GOT_PC_LO12))) + return; + + // GOT references to absolute symbols can't be relaxed to use pcaddi in + // position-independent code, because these instructions produce a relative + // address. + // Meanwhile skip undefined, preemptible and STT_GNU_IFUNC symbols, because + // these symbols may be resolve in runtime. + if (rHi20.type == R_LARCH_GOT_PC_HI20 && + (!rHi20.sym->isDefined() || rHi20.sym->isPreemptible || + rHi20.sym->isGnuIFunc() || + (ctx.arg.isPic && !cast(*rHi20.sym).section))) + return; + + uint64_t dest = 0; + if (rHi20.expr == RE_LOONGARCH_PLT_PAGE_PC) + dest = rHi20.sym->getPltVA(ctx); + else if (rHi20.expr == RE_LOONGARCH_PAGE_PC || + rHi20.expr == RE_LOONGARCH_GOT_PAGE_PC) + dest = rHi20.sym->getVA(ctx); + else { + Err(ctx) << getErrorLoc(ctx, (const uint8_t *)loc) << "unknown expr (" + << rHi20.expr << ") against symbol " << rHi20.sym + << "in relaxPCHi20Lo12"; + return; + } + dest += rHi20.addend; + + const int64_t displace = dest - loc; + // Check if the displace aligns 4 bytes or exceeds the range of pcaddi. + if ((displace & 0x3) != 0 || !isInt<22>(displace)) + return; + + // Note: If we can ensure that the .o files generated by LLVM only contain + // relaxable instruction sequences with R_LARCH_RELAX, then we do not need to + // decode instructions. The relaxable instruction sequences imply the + // following constraints: + // * For relocation pairs related to got_pc, the opcodes of instructions + // must be pcalau12i + ld.w/d. In other cases, the opcodes must be pcalau12i + + // addi.w/d. + // * The destination register of pcalau12i is guaranteed to be used only by + // the immediately following instruction. + const uint32_t currInsn = read32le(sec.content().data() + rHi20.offset); + const uint32_t nextInsn = read32le(sec.content().data() + rLo12.offset); + // Check if use the same register. + if (getD5(currInsn) != getJ5(nextInsn) || getJ5(nextInsn) != getD5(nextInsn)) + return; + + sec.relaxAux->relocTypes[i] = R_LARCH_RELAX; + sec.relaxAux->relocTypes[i + 2] = R_LARCH_PCREL20_S2; + sec.relaxAux->writes.push_back(insn(PCADDI, getD5(nextInsn), 0, 0)); + remove = 4; +} + static bool relax(Ctx &ctx, InputSection &sec) { const uint64_t secAddr = sec.getVA(); const MutableArrayRef relocs = sec.relocs(); @@ -781,6 +868,12 @@ static bool relax(Ctx &ctx, InputSection &sec) { } break; } + case R_LARCH_PCALA_HI20: + case R_LARCH_GOT_PC_HI20: + // The overflow check for i+2 will be carried out in isPairRelaxable. + if (isPairRelaxable(relocs, i)) + relaxPCHi20Lo12(ctx, sec, i, loc, r, relocs[i + 2], remove); + break; } // For all anchors whose offsets are <= r.offset, they are preceded by @@ -851,6 +944,7 @@ void LoongArch::finalizeRelax(int passes) const { MutableArrayRef rels = sec->relocs(); ArrayRef old = sec->content(); size_t newSize = old.size() - aux.relocDeltas[rels.size() - 1]; + size_t writesIdx = 0; uint8_t *p = ctx.bAlloc.Allocate(newSize); uint64_t offset = 0; int64_t delta = 0; @@ -867,11 +961,29 @@ void LoongArch::finalizeRelax(int passes) const { continue; // Copy from last location to the current relocated location. - const Relocation &r = rels[i]; + Relocation &r = rels[i]; uint64_t size = r.offset - offset; memcpy(p, old.data() + offset, size); p += size; - offset = r.offset + remove; + + int64_t skip = 0; + if (RelType newType = aux.relocTypes[i]) { + switch (newType) { + case R_LARCH_RELAX: + break; + case R_LARCH_PCREL20_S2: + skip = 4; + write32le(p, aux.writes[writesIdx++]); + // RelExpr is needed for relocating. + r.expr = r.sym->hasFlag(NEEDS_PLT) ? R_PLT_PC : R_PC; + break; + default: + llvm_unreachable("unsupported type"); + } + } + + p += skip; + offset = r.offset + skip + remove; } memcpy(p, old.data() + offset, old.size() - offset); diff --git a/lld/test/ELF/loongarch-relax-align.s b/lld/test/ELF/loongarch-relax-align.s index ab61e15d5caca..79353f2a3be47 100644 --- a/lld/test/ELF/loongarch-relax-align.s +++ b/lld/test/ELF/loongarch-relax-align.s @@ -4,58 +4,93 @@ # 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 +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.text2=0x20000 -e 0 --no-relax %t.32.o -o %t.32n +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.text2=0x20000 -e 0 --no-relax %t.64.o -o %t.64n +# RUN: llvm-objdump -td --no-show-raw-insn %t.32 | FileCheck --check-prefixes=RELAX,NOOLD %s +# RUN: llvm-objdump -td --no-show-raw-insn %t.64 | FileCheck --check-prefixes=RELAX,NOOLD %s +# RUN: llvm-objdump -td --no-show-raw-insn %t.32n | FileCheck --check-prefixes=NORELAX,NOOLD %s +# RUN: llvm-objdump -td --no-show-raw-insn %t.64n | FileCheck --check-prefixes=NORELAX,NOOLD %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 +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.text2=0x20000 -e 0 --no-relax %t.o64.o -o %t.o64n +# RUN: llvm-objdump -td --no-show-raw-insn %t.o64 | FileCheck --check-prefixes=RELAX,OLD %s +# RUN: llvm-objdump -td --no-show-raw-insn %t.o64n | FileCheck --check-prefixes=NORELAX,OLD %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 +# NOOLD: {{0*}}10000 l .text {{0*}}00 .Lalign_symbol +# OLD: {{0*}}00001 l *ABS* {{0*}}00 old + +# NORELAX-DAG: {{0*}}10000 l .text {{0*}}44 .Ltext_start +# NORELAX-DAG: {{0*}}10038 l .text {{0*}}0c .L1 +# NORELAX-DAG: {{0*}}10040 l .text {{0*}}04 .L2 +# NORELAX-DAG: {{0*}}20000 l .text2 {{0*}}14 .Ltext2_start + +# NORELAX: <.Ltext_start>: +# NORELAX-NEXT: break 1 +# NORELAX-NEXT: break 2 +# NORELAX-NEXT: nop +# NORELAX-NEXT: nop +# NORELAX-NEXT: break 3 +# NORELAX-NEXT: break 4 +# NORELAX-NEXT: nop +# NORELAX-NEXT: nop +# NORELAX-NEXT: pcalau12i $a0, 0 +# NORELAX-NEXT: addi.{{[dw]}} $a0, $a0, 0 +# NORELAX-NEXT: pcalau12i $a0, 0 +# NORELAX-NEXT: addi.{{[dw]}} $a0, $a0, 56 +# NORELAX-NEXT: pcalau12i $a0, 0 +# NORELAX-NEXT: addi.{{[dw]}} $a0, $a0, 64 +# NORELAX-EMPTY: +# NORELAX-NEXT: <.L1>: +# NORELAX-NEXT: nop +# NORELAX-NEXT: nop +# NORELAX-EMPTY: +# NORELAX-NEXT: <.L2>: +# NORELAX-NEXT: break 5 + +# NORELAX: <.Ltext2_start>: +# NORELAX-NEXT: pcalau12i $a0, 0 +# NORELAX-NEXT: addi.{{[dw]}} $a0, $a0, 0 +# NORELAX-NEXT: nop +# NORELAX-NEXT: nop +# NORELAX-NEXT: break 6 + + +# RELAX-DAG: {{0*}}10000 l .text {{0*}}34 .Ltext_start +# RELAX-DAG: {{0*}}1002c l .text {{0*}}08 .L1 +# RELAX-DAG: {{0*}}10030 l .text {{0*}}04 .L2 +# RELAX-DAG: {{0*}}20000 l .text2 {{0*}}14 .Ltext2_start + +# RELAX: <.Ltext_start>: +# RELAX-NEXT: break 1 +# RELAX-NEXT: break 2 +# RELAX-NEXT: nop +# RELAX-NEXT: nop +# RELAX-NEXT: break 3 +# RELAX-NEXT: break 4 +# RELAX-NEXT: nop +# RELAX-NEXT: nop +# RELAX-NEXT: pcaddi $a0, -8 +# RELAX-NEXT: pcaddi $a0, 2 +# RELAX-NEXT: pcaddi $a0, 2 +# RELAX-EMPTY: +# RELAX-NEXT: <.L1>: +# RELAX-NEXT: nop +# RELAX-EMPTY: +# RELAX-NEXT: <.L2>: +# RELAX-NEXT: break 5 + +# RELAX: <.Ltext2_start>: +# RELAX-NEXT: pcaddi $a0, 0 +# RELAX-NEXT: nop +# RELAX-NEXT: nop +# RELAX-NEXT: nop +# RELAX-NEXT: break 6 # CHECKR: <.Ltext2_start>: # CHECKR-NEXT: pcalau12i $a0, 0 diff --git a/lld/test/ELF/loongarch-relax-emit-relocs.s b/lld/test/ELF/loongarch-relax-emit-relocs.s index ba414e8c93f0f..7031ffd9f6686 100644 --- a/lld/test/ELF/loongarch-relax-emit-relocs.s +++ b/lld/test/ELF/loongarch-relax-emit-relocs.s @@ -3,39 +3,64 @@ # 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 +# RUN: ld.lld -Ttext=0x10000 -section-start=.got=0x20000 --emit-relocs %t.32.o -o %t.32 +# RUN: ld.lld -Ttext=0x10000 -section-start=.got=0x20000 --emit-relocs %t.64.o -o %t.64 +# RUN: llvm-objdump -dr %t.32 | FileCheck %s --check-prefix=RELAX +# RUN: llvm-objdump -dr %t.64 | FileCheck %s --check-prefix=RELAX ## -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 +# RUN: ld.lld -Ttext=0x10000 -section-start=.got=0x20000 --emit-relocs --no-relax %t.64.o -o %t.64.norelax +# RUN: llvm-objdump -dr %t.64.norelax | FileCheck %s --check-prefix=NORELAX -# 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 *ABS*+0xc -# CHECK-NEXT: nop -# CHECK-NEXT: ret +# RELAX: 00010000 <_start>: +# RELAX-NEXT: pcaddi $a0, 0 +# RELAX-NEXT: R_LARCH_RELAX _start +# RELAX-NEXT: R_LARCH_RELAX *ABS* +# RELAX-NEXT: R_LARCH_PCREL20_S2 _start +# RELAX-NEXT: R_LARCH_RELAX *ABS* +# RELAX-NEXT: pcaddi $a0, -1 +# RELAX-NEXT: R_LARCH_RELAX _start +# RELAX-NEXT: R_LARCH_RELAX *ABS* +# RELAX-NEXT: R_LARCH_PCREL20_S2 _start +# RELAX-NEXT: R_LARCH_RELAX *ABS* +# RELAX-NEXT: nop +# RELAX-NEXT: R_LARCH_ALIGN *ABS*+0xc +# RELAX-NEXT: nop +# RELAX-NEXT: ret + +# NORELAX: <_start>: +# NORELAX-NEXT: pcalau12i $a0, 0 +# NORELAX-NEXT: R_LARCH_PCALA_HI20 _start +# NORELAX-NEXT: R_LARCH_RELAX *ABS* +# NORELAX-NEXT: addi.d $a0, $a0, 0 +# NORELAX-NEXT: R_LARCH_PCALA_LO12 _start +# NORELAX-NEXT: R_LARCH_RELAX *ABS* +# NORELAX-NEXT: pcalau12i $a0, 16 +# NORELAX-NEXT: R_LARCH_GOT_PC_HI20 _start +# NORELAX-NEXT: R_LARCH_RELAX *ABS* +# NORELAX-NEXT: ld.d $a0, $a0, 0 +# NORELAX-NEXT: R_LARCH_GOT_PC_LO12 _start +# NORELAX-NEXT: R_LARCH_RELAX *ABS* +# NORELAX-NEXT: ret +# NORELAX-NEXT: R_LARCH_ALIGN *ABS*+0xc # 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: addi.d $a0, $a0, 0 # CHECKR-NEXT: R_LARCH_PCALA_LO12 _start # CHECKR-NEXT: R_LARCH_RELAX *ABS* +# CHECKR-NEXT: pcalau12i $a0, 0 +# CHECKR-NEXT: R_LARCH_GOT_PC_HI20 _start +# CHECKR-NEXT: R_LARCH_RELAX *ABS* +# CHECKR-NEXT: ld.d $a0, $a0, 0 +# CHECKR-NEXT: R_LARCH_GOT_PC_LO12 _start +# CHECKR-NEXT: R_LARCH_RELAX *ABS* # CHECKR-NEXT: nop # CHECKR-NEXT: R_LARCH_ALIGN *ABS*+0xc # CHECKR-NEXT: nop @@ -45,5 +70,6 @@ .global _start _start: la.pcrel $a0, _start + la.got $a0, _start .p2align 4 ret diff --git a/lld/test/ELF/loongarch-relax-pc-hi20-lo12-got-symbols.s b/lld/test/ELF/loongarch-relax-pc-hi20-lo12-got-symbols.s new file mode 100644 index 0000000000000..f37de8e3b7c83 --- /dev/null +++ b/lld/test/ELF/loongarch-relax-pc-hi20-lo12-got-symbols.s @@ -0,0 +1,90 @@ +## This test verifies that the pair pcalau12i + ld.w/d is relaxed/not relaxed +## depending on the target symbol properties. + +# REQUIRES: loongarch +# RUN: rm -rf %t && split-file %s %t && cd %t + +# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax symbols.s -o symbols.32.o +# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax symbols.s -o symbols.64.o +# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax abs.s -o abs.32.o +# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax abs.s -o abs.64.o + +# RUN: ld.lld --shared -Tlinker.t symbols.32.o abs.32.o -o symbols.32.so +# RUN: ld.lld --shared -Tlinker.t symbols.64.o abs.64.o -o symbols.64.so +# RUN: llvm-objdump -d --no-show-raw-insn symbols.32.so | FileCheck --check-prefixes=LIB %s +# RUN: llvm-objdump -d --no-show-raw-insn symbols.64.so | FileCheck --check-prefixes=LIB %s + +# RUN: ld.lld -Tlinker.t -z undefs symbols.32.o abs.32.o -o symbols.32 +# RUN: ld.lld -Tlinker.t -z undefs symbols.64.o abs.64.o -o symbols.64 +# RUN: llvm-objdump -d --no-show-raw-insn symbols.32 | FileCheck --check-prefixes=EXE %s +# RUN: llvm-objdump -d --no-show-raw-insn symbols.64 | FileCheck --check-prefixes=EXE %s + + +## Symbol 'hidden_sym' is nonpreemptible, the relaxation should be applied. +LIB: pcaddi $a0, [[#]] +## Symbol 'global_sym' is preemptible, no relaxations should be applied. +LIB-NEXT: pcalau12i $a1, 4 +LIB-NEXT: ld.{{[wd]}} $a1, $a1, [[#]] +## Symbol 'undefined_sym' is undefined, no relaxations should be applied. +LIB-NEXT: pcalau12i $a2, 4 +LIB-NEXT: ld.{{[wd]}} $a2, $a2, [[#]] +## Symbol 'ifunc_sym' is STT_GNU_IFUNC, no relaxations should be applied. +LIB-NEXT: pcalau12i $a3, 4 +LIB-NEXT: ld.{{[wd]}} $a3, $a3, [[#]] +## Symbol 'abs_sym' is absolute, no relaxations should be applied. +LIB-NEXT: pcalau12i $a4, 4 +LIB-NEXT: ld.{{[wd]}} $a4, $a4, [[#]] + + +## Symbol 'hidden_sym' is nonpreemptible, the relaxation should be applied. +EXE: pcaddi $a0, [[#]] +## Symbol 'global_sym' is nonpreemptible, the relaxation should be applied. +EXE-NEXT: pcaddi $a1, [[#]] +## Symbol 'undefined_sym' is undefined, no relaxations should be applied. +EXE-NEXT: pcalau12i $a2, 4 +EXE-NEXT: ld.{{[wd]}} $a2, $a2, [[#]] +## Symbol 'ifunc_sym' is STT_GNU_IFUNC, no relaxations should be applied. +EXE-NEXT: pcalau12i $a3, 4 +EXE-NEXT: ld.{{[wd]}} $a3, $a3, [[#]] +## Symbol 'abs_sym' is absolute, relaxations may be applied in -no-pie mode. +EXE-NEXT: pcaddi $a4, -[[#]] + + +## The linker script ensures that .rodata and .text are near (>4M) so that +## the pcalau12i+ld.w/d pair can be relaxed to pcaddi. +#--- linker.t +SECTIONS { + .text 0x10000: { *(.text) } + .rodata 0x14000: { *(.rodata) } +} + +# This symbol is defined in a separate file to prevent the definition from +# being folded into the instructions that reference it. +#--- abs.s +.global abs_sym +.hidden abs_sym +abs_sym = 0x1000 + +#--- symbols.s +.rodata +.hidden hidden_sym +hidden_sym: +.word 10 + +.global global_sym +global_sym: +.word 10 + +.text +.type ifunc_sym STT_GNU_IFUNC +.hidden ifunc_sym +ifunc_sym: + nop + +.global _start +_start: + la.got $a0, hidden_sym + la.got $a1, global_sym + la.got $a2, undefined_sym + la.got $a3, ifunc_sym + la.got $a4, abs_sym diff --git a/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s b/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s new file mode 100644 index 0000000000000..a417d89e9fa2e --- /dev/null +++ b/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s @@ -0,0 +1,62 @@ +# 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=.data=0x14000 %t.32.o -o %t.32 +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 %t.64.o -o %t.64 +# RUN: llvm-objdump -td --no-show-raw-insn %t.32 | FileCheck --check-prefixes=RELAX %s +# RUN: llvm-objdump -td --no-show-raw-insn %t.64 | FileCheck --check-prefixes=RELAX %s + +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 %t.32.o -shared -o %t.32s +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 %t.64.o -shared -o %t.64s +# RUN: llvm-objdump -td --no-show-raw-insn %t.32s | FileCheck --check-prefixes=RELAX %s +# RUN: llvm-objdump -td --no-show-raw-insn %t.64s | FileCheck --check-prefixes=RELAX %s + +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x410000 %t.32.o -o %t.32o +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x410000 %t.64.o -o %t.64o +# RUN: llvm-objdump -td --no-show-raw-insn %t.32o | FileCheck --check-prefixes=NORELAX32 %s +# RUN: llvm-objdump -td --no-show-raw-insn %t.64o | FileCheck --check-prefixes=NORELAX64 %s + +# RELAX-LABEL: <_start>: +## offset = 0x14000 - 0x10000 = 4096<<2 +# RELAX-NEXT: 10000: pcaddi $a0, 4096 +# RELAX-NEXT: pcaddi $a0, 4095 +# RELAX-NEXT: pcaddi $a0, 4094 +# RELAX-NEXT: pcaddi $a0, 4093 + +# NORELAX32-LABEL: <_start>: +## offset exceed range of pcaddi +## offset = 0x410000 - 0x10000: 0x400 pages, page offset 0 +# NORELAX32-NEXT: 10000: pcalau12i $a0, 1024 +# NORELAX32-NEXT: addi.w $a0, $a0, 0 +# NORELAX32-NEXT: pcalau12i $a0, 1024 +# NORELAX32-NEXT: ld.w $a0, $a0, 4 +# NORELAX32-NEXT: pcalau12i $a0, 1024 +# NORELAX32-NEXT: addi.w $a0, $a0, 0 +# NORELAX32-NEXT: pcalau12i $a0, 1024 +# NORELAX32-NEXT: ld.w $a0, $a0, 4 + +# NORELAX64-LABEL: <_start>: +## offset exceed range of pcaddi +## offset = 0x410000 - 0x10000: 0x400 pages, page offset 0 +# NORELAX64-NEXT: 10000: pcalau12i $a0, 1024 +# NORELAX64-NEXT: addi.d $a0, $a0, 0 +# NORELAX64-NEXT: pcalau12i $a0, 1024 +# NORELAX64-NEXT: ld.d $a0, $a0, 8 +# NORELAX64-NEXT: pcalau12i $a0, 1024 +# NORELAX64-NEXT: addi.d $a0, $a0, 0 +# NORELAX64-NEXT: pcalau12i $a0, 1024 +# NORELAX64-NEXT: ld.d $a0, $a0, 8 + +.section .text +.global _start +_start: + la.local $a0, sym + la.global $a0, sym + la.pcrel $a0, sym + la.got $a0, sym + +.section .data +sym: + .zero 4