[lld][LoongArch] Relax R_LARCH_{PCALA,GOT_PC}_{HI20,LO12} (#123566)

Support relaxation optimization for two types of code sequences.
```
From:
   pcalau12i $a0, %pc_hi20(sym)
       R_LARCH_PCALA_HI20, R_LARCH_RELAX
   addi.w/d $a0, $a0, %pc_lo12(sym)
       R_LARCH_PCALA_LO12, R_LARCH_RELAX
To:
   pcaddi $a0, %pc_lo12(sym)
       R_LARCH_PCREL20_S2
    
From:
   pcalau12i $a0, %got_pc_hi20(sym_got)
       R_LARCH_GOT_PC_HI20, R_LARCH_RELAX
   ld.w/d $a0, $a0, %got_pc_lo12(sym_got)
       R_LARCH_GOT_PC_LO12, R_LARCH_RELAX
To:
   pcaddi $a0, %got_pc_hi20(sym_got)
       R_LARCH_PCREL20_S2
```
Others:
- `loongarch-relax-pc-hi20-lo12-got-symbols.s` is inspired by
`aarch64-adrp-ldr-got-symbols.s`.

Co-authored-by: Xin Wang
[wangxin03@loongson.cn](mailto:wangxin03@loongson.cn)
This commit is contained in:
Zhaoxin Yang 2025-02-15 09:19:17 +08:00 committed by GitHub
parent 68a82a2298
commit 6c54ab548f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 387 additions and 62 deletions

View File

@ -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<Relocation> relocs, size_t i) {
return i + 1 < relocs.size() && relocs[i + 1].type == R_LARCH_RELAX;
}
static bool isPairRelaxable(ArrayRef<Relocation> 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<Defined>(*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<Relocation> 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<Relocation> rels = sec->relocs();
ArrayRef<uint8_t> old = sec->content();
size_t newSize = old.size() - aux.relocDeltas[rels.size() - 1];
size_t writesIdx = 0;
uint8_t *p = ctx.bAlloc.Allocate<uint8_t>(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);

View File

@ -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
# NOOLD: {{0*}}10000 l .text {{0*}}00 .Lalign_symbol
# OLD: {{0*}}00001 l *ABS* {{0*}}00 old
# 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
# 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
# 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
# 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

View File

@ -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

View File

@ -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

View File

@ -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