-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[LoongArch] Add reloc types for LA32R/LA32S #146499
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-llvm-binary-utilities @llvm/pr-subscribers-backend-loongarch Author: hev (heiher) ChangesLink: loongson/la-abi-specs#16 Patch is 196.86 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/146499.diff 55 Files Affected:
diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index 3c4ad53af1b51..fa79c8df39f1b 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -161,6 +161,10 @@ static uint32_t setJ5(uint32_t insn, uint32_t imm) {
return (insn & 0xfffffc1f) | (extractBits(imm, 4, 0) << 5);
}
+static uint32_t setK10(uint32_t insn, uint32_t imm) {
+ return (insn & 0xffc003ff) | (extractBits(imm, 9, 0) << 10);
+}
+
static uint32_t setK12(uint32_t insn, uint32_t imm) {
return (insn & 0xffc003ff) | (extractBits(imm, 11, 0) << 10);
}
@@ -416,6 +420,8 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
// [1]: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=9f482b73f41a9a1bbfb173aad0733d1c824c788a
// [2]: https://github.com/loongson/la-abi-specs/pull/3
return isJirl(read32le(loc)) ? R_PLT : R_ABS;
+ case R_LARCH_PCADD_LO12_I:
+ return RE_LOONGARCH_PC_INDIRECT;
case R_LARCH_TLS_DTPREL32:
case R_LARCH_TLS_DTPREL64:
return R_DTPREL;
@@ -446,10 +452,12 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_32_PCREL:
case R_LARCH_64_PCREL:
case R_LARCH_PCREL20_S2:
+ case R_LARCH_PCADD_HI20:
return R_PC;
case R_LARCH_B16:
case R_LARCH_B21:
case R_LARCH_B26:
+ case R_LARCH_CALL30:
case R_LARCH_CALL36:
return R_PLT_PC;
case R_LARCH_GOT_PC_HI20:
@@ -459,6 +467,9 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_TLS_IE64_PC_LO20:
case R_LARCH_TLS_IE64_PC_HI12:
return RE_LOONGARCH_GOT_PAGE_PC;
+ case R_LARCH_PCADD_GOT_HI20:
+ case R_LARCH_PCADD_TLS_IE_HI20:
+ return R_GOT_PC;
case R_LARCH_GOT_PC_LO12:
case R_LARCH_TLS_IE_PC_LO12:
return RE_LOONGARCH_GOT;
@@ -522,6 +533,7 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_TLS_DESC_LO12:
case R_LARCH_TLS_DESC64_LO20:
case R_LARCH_TLS_DESC64_HI12:
+ case R_LARCH_PCADD_TLS_DESC_HI20:
return R_TLSDESC;
case R_LARCH_TLS_DESC_CALL:
return R_TLSDESC_CALL;
@@ -605,6 +617,22 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
write32le(loc, setD10k16(read32le(loc), val >> 2));
return;
+ case R_LARCH_CALL30: {
+ // This relocation is designed for adjacent pcaddu12i+jirl pairs that
+ // are patched in one time.
+ // The relocation range is [-4G, +4G) (of course must be 4-byte aligned).
+ if ((int64_t)val != llvm::SignExtend64(val, 32))
+ reportRangeError(ctx, loc, rel, Twine(val), llvm::minIntN(32),
+ llvm::maxIntN(32));
+ checkAlignment(ctx, loc, val, 4, rel);
+ uint32_t hi20 = extractBits(val, 31, 12);
+ // Despite the name, the lower part is actually 12 bits with 4-byte aligned.
+ uint32_t lo10 = extractBits(val, 11, 2);
+ write32le(loc, setJ20(read32le(loc), hi20));
+ write32le(loc + 4, setK10(read32le(loc + 4), lo10));
+ return;
+ }
+
case R_LARCH_CALL36: {
// This relocation is designed for adjacent pcaddu18i+jirl pairs that
// are patched in one time. Because of sign extension of these insns'
@@ -648,6 +676,7 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
case R_LARCH_TLS_LE_LO12_R:
case R_LARCH_TLS_DESC_PC_LO12:
case R_LARCH_TLS_DESC_LO12:
+ case R_LARCH_PCADD_LO12_I:
write32le(loc, setK12(read32le(loc), extractBits(val, 11, 0)));
return;
@@ -667,6 +696,15 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
case R_LARCH_TLS_DESC_HI20:
write32le(loc, setJ20(read32le(loc), extractBits(val, 31, 12)));
return;
+ case R_LARCH_PCADD_HI20:
+ case R_LARCH_PCADD_GOT_HI20:
+ case R_LARCH_PCADD_TLS_IE_HI20:
+ case R_LARCH_PCADD_TLS_DESC_HI20: {
+ uint64_t hi = val + 0x800;
+ checkInt(ctx, loc, SignExtend64(hi, 32) >> 12, 20, rel);
+ write32le(loc, setJ20(read32le(loc), extractBits(hi, 31, 12)));
+ return;
+ }
case R_LARCH_TLS_LE_HI20_R:
write32le(loc, setJ20(read32le(loc), extractBits(val + 0x800, 31, 12)));
return;
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index f8786265029e8..777209d5b20cf 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -713,6 +713,61 @@ static Relocation *getRISCVPCRelHi20(Ctx &ctx, const InputSectionBase *loSec,
return nullptr;
}
+// For RE_LARCH_PC_INDIRECT (R_LARCH_PCADD_LO12_I), the symbol actually
+// points the corresponding R_LARCH_PCADD_*_HI20 relocation, and the target VA
+// is calculated using PCADD_HI20's symbol.
+//
+// This function returns the R_LARCH_PCADD_*_HI20 relocation from the
+// R_LARCH_PCADD_LO12 relocation.
+static Relocation *getLoongArchPCAddHi20(Ctx &ctx,
+ const InputSectionBase *loSec,
+ const Relocation &loReloc) {
+ int64_t addend = loReloc.addend;
+ Symbol *sym = loReloc.sym;
+
+ const Defined *d = cast<Defined>(sym);
+ if (!d->section) {
+ Err(ctx) << loSec->getLocation(loReloc.offset)
+ << ": R_LARCH_PCADD_LO12 relocation points to an absolute symbol: "
+ << sym->getName();
+ return nullptr;
+ }
+ InputSection *hiSec = cast<InputSection>(d->section);
+
+ if (hiSec != loSec)
+ Err(ctx) << loSec->getLocation(loReloc.offset)
+ << ": R_LARCH_PCADD_LO12 relocation points to a symbol '"
+ << sym->getName() << "' in a different section '" << hiSec->name
+ << "'";
+
+ if (addend != 0)
+ Warn(ctx) << loSec->getLocation(loReloc.offset)
+ << ": non-zero addend in R_LARCH_PCADD_LO12 relocation to "
+ << hiSec->getObjMsg(d->value) << " is ignored";
+
+ // Relocations are sorted by offset, so we can use std::equal_range to do
+ // binary search.
+ Relocation hiReloc;
+ hiReloc.offset = d->value + addend;
+ auto range =
+ std::equal_range(hiSec->relocs().begin(), hiSec->relocs().end(), hiReloc,
+ [](const Relocation &lhs, const Relocation &rhs) {
+ return lhs.offset < rhs.offset;
+ });
+
+ for (auto it = range.first; it != range.second; ++it)
+ if (it->type == R_LARCH_PCADD_HI20 || it->type == R_LARCH_PCADD_GOT_HI20 ||
+ it->type == R_LARCH_PCADD_TLS_IE_HI20 ||
+ it->type == R_LARCH_PCADD_TLS_DESC_HI20)
+ return &*it;
+
+ Err(ctx) << loSec->getLocation(loReloc.offset)
+ << ": R_LARCH_PCADD_LO12 relocation points to "
+ << hiSec->getObjMsg(d->value)
+ << " without an associated R_LARCH_PCADD_HI20 relocation";
+ return nullptr;
+}
+
// A TLS symbol's virtual address is relative to the TLS segment. Add a
// target-specific adjustment to produce a thread-pointer-relative offset.
static int64_t getTlsTpOffset(Ctx &ctx, const Symbol &s) {
@@ -884,6 +939,11 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
return getRelocTargetVA(ctx, *hiRel, r.sym->getVA(ctx));
return 0;
}
+ case RE_LOONGARCH_PC_INDIRECT: {
+ if (const Relocation *hiRel = getLoongArchPCAddHi20(ctx, this, r))
+ return getRelocTargetVA(ctx, *hiRel, r.sym->getVA(ctx, a));
+ return 0;
+ }
case RE_LOONGARCH_PAGE_PC:
return getLoongArchPageDelta(r.sym->getVA(ctx, a), p, r.type);
case R_PC:
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 43f19186f0981..5b12f40c8d5fc 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -210,7 +210,7 @@ static bool isRelExpr(RelExpr expr) {
return oneof<R_PC, R_GOTREL, R_GOTPLTREL, RE_ARM_PCA, RE_MIPS_GOTREL,
RE_PPC64_CALL, RE_PPC64_RELAX_TOC, RE_AARCH64_PAGE_PC,
R_RELAX_GOT_PC, RE_RISCV_PC_INDIRECT, RE_PPC64_RELAX_GOT_PC,
- RE_LOONGARCH_PAGE_PC>(expr);
+ RE_LOONGARCH_PAGE_PC, RE_LOONGARCH_PC_INDIRECT>(expr);
}
static RelExpr toPlt(RelExpr expr) {
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index d2a77bc953109..3213c9387606e 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -129,6 +129,7 @@ enum RelExpr {
// also reused for TLS, making the semantics differ from other architectures.
RE_LOONGARCH_GOT,
RE_LOONGARCH_GOT_PAGE_PC,
+ RE_LOONGARCH_PC_INDIRECT,
RE_LOONGARCH_TLSGD_PAGE_PC,
RE_LOONGARCH_TLSDESC_PAGE_PC,
};
diff --git a/lld/test/ELF/loongarch-call30.s b/lld/test/ELF/loongarch-call30.s
new file mode 100644
index 0000000000000..907e8704e908b
--- /dev/null
+++ b/lld/test/ELF/loongarch-call30.s
@@ -0,0 +1,64 @@
+# REQUIRES: loongarch
+
+# RUN: rm -rf %t && split-file %s %t
+# RUN: llvm-mc --filetype=obj --triple=loongarch32-unknown-elf %t/a.s -o %t/a.o
+
+# RUN: ld.lld %t/a.o --section-start=.text=0x20010 --section-start=.sec.foo=0x21020 -o %t/exe1
+# RUN: llvm-objdump --no-show-raw-insn -d %t/exe1 | FileCheck --match-full-lines %s --check-prefix=EXE1
+## hi20 = target - pc >> 12 = 0x21020 - 0x20010 >> 12 = 1
+## lo12 = target - pc & (1 << 12) - 1 = 0x21020 - 0x20010 & 0xfff = 16
+# EXE1: 20010: pcaddu12i $t0, 1
+# EXE1-NEXT: 20014: jirl $zero, $t0, 16
+
+# RUN: ld.lld %t/a.o --section-start=.text=0x20010 --section-start=.sec.foo=0x21820 -o %t/exe2
+# RUN: llvm-objdump --no-show-raw-insn -d %t/exe2 | FileCheck --match-full-lines %s --check-prefix=EXE2
+## hi20 = target - pc >> 12 = 0x21820 - 0x20010 >> 12 = 1
+## lo12 = target - pc & (1 << 12) - 1 = 0x21820 - 0x20010 & 0xfff = 2064
+# EXE2: 20010: pcaddu12i $t0, 1
+# EXE2-NEXT: 20014: jirl $zero, $t0, 2064
+
+# RUN: ld.lld %t/a.o -shared -T %t/a.t -o %t/a.so
+# RUN: llvm-readelf -x .got.plt %t/a.so | FileCheck --check-prefix=GOTPLT %s
+# RUN: llvm-objdump -d --no-show-raw-insn %t/a.so | FileCheck --check-prefix=SO %s
+## PLT should be present in this case.
+# SO: Disassembly of section .plt:
+# SO: <.plt>:
+## foo@plt:
+# SO: 1234520: pcaddu12i $t3, 64{{$}}
+# SO-NEXT: ld.w $t3, $t3, 444{{$}}
+# SO-NEXT: jirl $t1, $t3, 0
+# SO-NEXT: nop
+
+# SO: Disassembly of section .text:
+# SO: <_start>:
+## hi20 = foo@plt - pc >> 12 = 0x1234520 - 0x1274670 >> 12 = -65
+## lo18 = foo@plt - pc & (1 << 12) - 1 = 0x1234520 - 0x1274670 & 0xfff = 3760
+# SO-NEXT: pcaddu12i $t0, -65{{$}}
+# SO-NEXT: jirl $zero, $t0, 3760{{$}}
+
+# GOTPLT: section '.got.plt':
+# GOTPLT-NEXT: 0x012746d4 00000000 00000000 00452301
+
+## Impossible case in reality becasue all LoongArch instructions are fixed 4-bytes long.
+# RUN: not ld.lld %t/a.o --section-start=.text=0x20000 --section-start=.sec.foo=0x40001 -o /dev/null 2>&1 | \
+# RUN: FileCheck -DFILE=%t/a.o --check-prefix=ERROR-ALIGN %s
+# ERROR-ALIGN: error: [[FILE]]:(.text+0x0): improper alignment for relocation R_LARCH_CALL30: 0x20001 is not aligned to 4 bytes
+
+#--- a.t
+SECTIONS {
+ .plt 0x1234500: { *(.plt) }
+ .text 0x1274670: { *(.text) }
+}
+
+#--- a.s
+.text
+.global _start
+_start:
+ .reloc ., R_LARCH_CALL30, foo
+ pcaddu12i $t0, 0
+ jirl $zero, $t0, 0
+
+.section .sec.foo,"awx"
+.global foo
+foo:
+ ret
diff --git a/lld/test/ELF/loongarch-relax-align.s b/lld/test/ELF/loongarch-relax-align.s
index 79353f2a3be47..b9da1322a8c00 100644
--- a/lld/test/ELF/loongarch-relax-align.s
+++ b/lld/test/ELF/loongarch-relax-align.s
@@ -1,7 +1,7 @@
# 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: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+32s,+relax %s -o %t.32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+32s,+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 --no-relax %t.32.o -o %t.32n
diff --git a/lld/test/ELF/loongarch-relax-emit-relocs.s b/lld/test/ELF/loongarch-relax-emit-relocs.s
index 909b65075a695..6e1e85c004439 100644
--- a/lld/test/ELF/loongarch-relax-emit-relocs.s
+++ b/lld/test/ELF/loongarch-relax-emit-relocs.s
@@ -1,7 +1,7 @@
# 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=loongarch32 --mattr=+32s,+relax %s -o %t.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax --defsym ELF64=1 %s -o %t.64.o
# 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
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
index f37de8e3b7c83..fe243397af346 100644
--- a/lld/test/ELF/loongarch-relax-pc-hi20-lo12-got-symbols.s
+++ b/lld/test/ELF/loongarch-relax-pc-hi20-lo12-got-symbols.s
@@ -4,9 +4,9 @@
# 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=loongarch32 -mattr=+32s,+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=loongarch32 -mattr=+32s,+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
diff --git a/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s b/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
index a417d89e9fa2e..a1d6c0f7eec73 100644
--- a/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
+++ b/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
@@ -1,6 +1,6 @@
# REQUIRES: loongarch
-# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax %s -o %t.32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+32s,+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
diff --git a/lld/test/ELF/loongarch-tls-gd-edge-case.s b/lld/test/ELF/loongarch-tls-gd-edge-case.s
index 9f25f10c73b44..cfa65f91aebb8 100644
--- a/lld/test/ELF/loongarch-tls-gd-edge-case.s
+++ b/lld/test/ELF/loongarch-tls-gd-edge-case.s
@@ -24,8 +24,8 @@
# LA64-REL-NEXT: 00000000000203a8 0000000200000009 R_LARCH_TLS_DTPREL64 0000000000000000 y + 0
# LA64-REL-NEXT: 00000000000203b0 000000020000000b R_LARCH_TLS_TPREL64 0000000000000000 y + 0
-# LA32: 101d4: pcalau12i $a0, 16
-# LA32-NEXT: ld.w $a0, $a0, 580
+# LA32: 101d4: pcaddu12i $a0, 16
+# LA32-NEXT: ld.w $a0, $a0, 112
# LA32-NEXT: pcalau12i $a1, 16
# LA32-NEXT: addi.w $a1, $a1, 572
diff --git a/lld/test/ELF/loongarch-tls-ie.s b/lld/test/ELF/loongarch-tls-ie.s
index ddfd9c976cb9b..84e6672e31e05 100644
--- a/lld/test/ELF/loongarch-tls-ie.s
+++ b/lld/test/ELF/loongarch-tls-ie.s
@@ -41,11 +41,11 @@
## LA32:
## &.got[0] - . = 0x20214 - 0x101a4: 0x10 pages, page offset 0x214
## &.got[1] - . = 0x20218 - 0x101b0: 0x10 pages, page offset 0x218
-# IE32: 101a4: pcalau12i $a4, 16
-# IE32-NEXT: ld.w $a4, $a4, 532
+# IE32: 101a4: pcaddu12i $a4, 16
+# IE32-NEXT: ld.w $a4, $a4, 112
# IE32-NEXT: add.w $a4, $a4, $tp
-# IE32-NEXT: 101b0: pcalau12i $a5, 16
-# IE32-NEXT: ld.w $a5, $a5, 536
+# IE32: 101b0: pcaddu12i $a5, 16
+# IE32-NEXT: ld.w $a5, $a5, 104
# IE32-NEXT: add.w $a5, $a5, $tp
## LA64:
@@ -62,15 +62,16 @@
# a@tprel = st_value(a) = 0x8
# b@tprel = st_value(a) = 0xc
-# LE32-GOT: could not find section '.got'
+# LE32-GOT: section '.got':
+# LE32-GOT-NEXT: 0x0003012c 08000000 0c000000
# LE64-GOT: could not find section '.got'
## LA32:
-# LE32: 200d4: nop
-# LE32-NEXT: ori $a4, $zero, 8
+# LE32: 20114: pcaddu12i $a4, 16
+# LE32-NEXT: ld.w $a4, $a4, 24
# LE32-NEXT: add.w $a4, $a4, $tp
-# LE32-NEXT: 200e0: nop
-# LE32-NEXT: ori $a5, $zero, 12
+# LE32: 20120: pcaddu12i $a5, 16
+# LE32-NEXT: ld.w $a5, $a5, 16
# LE32-NEXT: add.w $a5, $a5, $tp
## LA64:
diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def
index 4859057abcbb9..ee5f6ed8c89b4 100644
--- a/llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def
+++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def
@@ -149,3 +149,14 @@ ELF_RELOC(R_LARCH_TLS_LE_LO12_R, 123)
ELF_RELOC(R_LARCH_TLS_LD_PCREL20_S2, 124)
ELF_RELOC(R_LARCH_TLS_GD_PCREL20_S2, 125)
ELF_RELOC(R_LARCH_TLS_DESC_PCREL20_S2, 126)
+
+// Relocs added in ELF for the LoongArch™ Architecture v2025????, part of the
+// v2.40 LoongArch ABI specs.
+//
+// Spec addition: https://github.com/loongson/la-abi-specs/pull/16
+ELF_RELOC(R_LARCH_CALL30, 127)
+ELF_RELOC(R_LARCH_PCADD_HI20, 128)
+ELF_RELOC(R_LARCH_PCADD_GOT_HI20, 129)
+ELF_RELOC(R_LARCH_PCADD_TLS_IE_HI20, 130)
+ELF_RELOC(R_LARCH_PCADD_TLS_DESC_HI20, 131)
+ELF_RELOC(R_LARCH_PCADD_LO12_I, 132)
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
index 04c5a67ac4fe3..1d9038a6fe2d8 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
@@ -170,6 +170,30 @@ enum EdgeKind_loongarch : Edge::Kind {
///
PageOffset12,
+ /// The upper 20 bits of the offset from the fixup to the target.
+ ///
+ /// Fixup expression:
+ /// Fixup <- (Target + Addend - Fixup + 0x800) >> 12 : int20
+ ///
+ /// Notes:
+ /// For PCADDU12I fixups.
+ ///
+ /// Errors:
+ /// - The result of the fixup expression must fit into an int20 otherwise an
+ /// out-of-range error will be returned.
+ ///
+ PCAdd20,
+
+ /// The lower 12 bits of the offset from the paired PCADDU12I (the initial
+ /// target) to the final target it points to.
+ ///
+ /// Typically used to fix up ADDI/LD_W/LD_D immediates.
+ ///
+ /// Fixup expression:
+ /// Fixup <- (FinalTarget - InitialTarget) & 0xfff : int12
+ ///
+ PCAdd12,
+
/// A GOT entry getter/constructor, transformed to Page20 pointing at the GOT
/// entry for the original target.
///
@@ -206,6 +230,49 @@ enum EdgeKind_loongarch : Edge::Kind {
///
RequestGOTAndTransformToPageOffset12,
+ /// A GOT entry getter/constructor, transformed to PCAdd20 pointing at the GOT
+ /// entry for the original target.
+ ///
+ /// Indicates that this edge should be transformed into a PCAdd20 targeting
+ /// the GOT entry for the edge's current target, maintaining the same addend.
+ /// A GOT entry for the target should be created if one does not already
+ /// exist.
+ ///
+ /// Edges of this kind are usually handled by a GOT/PLT builder pass inserted
+ /// by default.
+ ///
+ /// Fixup expression:
+ /// NONE
+ ///
+ /// Errors:
+ /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
+ /// phase will result in an assert/unreachable during the fixup phase.
+ ///
+ RequestGOTAndTransformToPCAdd20,
+
+ /// A 30-bit PC-relative call.
+ ///
+ /// Represents a PC-relative call to a target within [-4G, +4G)
+ /// The target must be 4-byte aligned. For adjacent pcaddu12i+jirl
+ /// instruction pairs.
+ ///
+ /// Fixup expression:
+ /// Fixup <- (Target - Fixup + Addend) >> 2 : int30
+ ///
+ /// Notes:
+ /// The '30' in the name refers to the number operand bits and follows the
+ /// naming convention used by the corresponding ELF relocations. Since the low
+ /// two bits must be zero (because of the 4-byte alignment of the target) the
+ /// operand is effectively a signed 32-bit number.
+ ///
+ /// Errors:
+ /// - The result of the unshifted part of the fixup expression must be
+ /// 4-byte aligned otherwise an alignment error will be returned.
+ /// - The result of the fixup expression must fit into an int30 otherwise an
+ /// out-of-range error will be returned.
+ ///
+ Call30PCRel,
+
/// A 36-bit PC-relative call.
///
/// Represents a PC-relative call to a target within [-128G - 0x20000, +128G
...
[truncated]
|
@llvm/pr-subscribers-lld-elf Author: hev (heiher) ChangesLink: loongson/la-abi-specs#16 Patch is 196.86 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/146499.diff 55 Files Affected:
diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index 3c4ad53af1b51..fa79c8df39f1b 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -161,6 +161,10 @@ static uint32_t setJ5(uint32_t insn, uint32_t imm) {
return (insn & 0xfffffc1f) | (extractBits(imm, 4, 0) << 5);
}
+static uint32_t setK10(uint32_t insn, uint32_t imm) {
+ return (insn & 0xffc003ff) | (extractBits(imm, 9, 0) << 10);
+}
+
static uint32_t setK12(uint32_t insn, uint32_t imm) {
return (insn & 0xffc003ff) | (extractBits(imm, 11, 0) << 10);
}
@@ -416,6 +420,8 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
// [1]: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=9f482b73f41a9a1bbfb173aad0733d1c824c788a
// [2]: https://github.com/loongson/la-abi-specs/pull/3
return isJirl(read32le(loc)) ? R_PLT : R_ABS;
+ case R_LARCH_PCADD_LO12_I:
+ return RE_LOONGARCH_PC_INDIRECT;
case R_LARCH_TLS_DTPREL32:
case R_LARCH_TLS_DTPREL64:
return R_DTPREL;
@@ -446,10 +452,12 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_32_PCREL:
case R_LARCH_64_PCREL:
case R_LARCH_PCREL20_S2:
+ case R_LARCH_PCADD_HI20:
return R_PC;
case R_LARCH_B16:
case R_LARCH_B21:
case R_LARCH_B26:
+ case R_LARCH_CALL30:
case R_LARCH_CALL36:
return R_PLT_PC;
case R_LARCH_GOT_PC_HI20:
@@ -459,6 +467,9 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_TLS_IE64_PC_LO20:
case R_LARCH_TLS_IE64_PC_HI12:
return RE_LOONGARCH_GOT_PAGE_PC;
+ case R_LARCH_PCADD_GOT_HI20:
+ case R_LARCH_PCADD_TLS_IE_HI20:
+ return R_GOT_PC;
case R_LARCH_GOT_PC_LO12:
case R_LARCH_TLS_IE_PC_LO12:
return RE_LOONGARCH_GOT;
@@ -522,6 +533,7 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_TLS_DESC_LO12:
case R_LARCH_TLS_DESC64_LO20:
case R_LARCH_TLS_DESC64_HI12:
+ case R_LARCH_PCADD_TLS_DESC_HI20:
return R_TLSDESC;
case R_LARCH_TLS_DESC_CALL:
return R_TLSDESC_CALL;
@@ -605,6 +617,22 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
write32le(loc, setD10k16(read32le(loc), val >> 2));
return;
+ case R_LARCH_CALL30: {
+ // This relocation is designed for adjacent pcaddu12i+jirl pairs that
+ // are patched in one time.
+ // The relocation range is [-4G, +4G) (of course must be 4-byte aligned).
+ if ((int64_t)val != llvm::SignExtend64(val, 32))
+ reportRangeError(ctx, loc, rel, Twine(val), llvm::minIntN(32),
+ llvm::maxIntN(32));
+ checkAlignment(ctx, loc, val, 4, rel);
+ uint32_t hi20 = extractBits(val, 31, 12);
+ // Despite the name, the lower part is actually 12 bits with 4-byte aligned.
+ uint32_t lo10 = extractBits(val, 11, 2);
+ write32le(loc, setJ20(read32le(loc), hi20));
+ write32le(loc + 4, setK10(read32le(loc + 4), lo10));
+ return;
+ }
+
case R_LARCH_CALL36: {
// This relocation is designed for adjacent pcaddu18i+jirl pairs that
// are patched in one time. Because of sign extension of these insns'
@@ -648,6 +676,7 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
case R_LARCH_TLS_LE_LO12_R:
case R_LARCH_TLS_DESC_PC_LO12:
case R_LARCH_TLS_DESC_LO12:
+ case R_LARCH_PCADD_LO12_I:
write32le(loc, setK12(read32le(loc), extractBits(val, 11, 0)));
return;
@@ -667,6 +696,15 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
case R_LARCH_TLS_DESC_HI20:
write32le(loc, setJ20(read32le(loc), extractBits(val, 31, 12)));
return;
+ case R_LARCH_PCADD_HI20:
+ case R_LARCH_PCADD_GOT_HI20:
+ case R_LARCH_PCADD_TLS_IE_HI20:
+ case R_LARCH_PCADD_TLS_DESC_HI20: {
+ uint64_t hi = val + 0x800;
+ checkInt(ctx, loc, SignExtend64(hi, 32) >> 12, 20, rel);
+ write32le(loc, setJ20(read32le(loc), extractBits(hi, 31, 12)));
+ return;
+ }
case R_LARCH_TLS_LE_HI20_R:
write32le(loc, setJ20(read32le(loc), extractBits(val + 0x800, 31, 12)));
return;
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index f8786265029e8..777209d5b20cf 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -713,6 +713,61 @@ static Relocation *getRISCVPCRelHi20(Ctx &ctx, const InputSectionBase *loSec,
return nullptr;
}
+// For RE_LARCH_PC_INDIRECT (R_LARCH_PCADD_LO12_I), the symbol actually
+// points the corresponding R_LARCH_PCADD_*_HI20 relocation, and the target VA
+// is calculated using PCADD_HI20's symbol.
+//
+// This function returns the R_LARCH_PCADD_*_HI20 relocation from the
+// R_LARCH_PCADD_LO12 relocation.
+static Relocation *getLoongArchPCAddHi20(Ctx &ctx,
+ const InputSectionBase *loSec,
+ const Relocation &loReloc) {
+ int64_t addend = loReloc.addend;
+ Symbol *sym = loReloc.sym;
+
+ const Defined *d = cast<Defined>(sym);
+ if (!d->section) {
+ Err(ctx) << loSec->getLocation(loReloc.offset)
+ << ": R_LARCH_PCADD_LO12 relocation points to an absolute symbol: "
+ << sym->getName();
+ return nullptr;
+ }
+ InputSection *hiSec = cast<InputSection>(d->section);
+
+ if (hiSec != loSec)
+ Err(ctx) << loSec->getLocation(loReloc.offset)
+ << ": R_LARCH_PCADD_LO12 relocation points to a symbol '"
+ << sym->getName() << "' in a different section '" << hiSec->name
+ << "'";
+
+ if (addend != 0)
+ Warn(ctx) << loSec->getLocation(loReloc.offset)
+ << ": non-zero addend in R_LARCH_PCADD_LO12 relocation to "
+ << hiSec->getObjMsg(d->value) << " is ignored";
+
+ // Relocations are sorted by offset, so we can use std::equal_range to do
+ // binary search.
+ Relocation hiReloc;
+ hiReloc.offset = d->value + addend;
+ auto range =
+ std::equal_range(hiSec->relocs().begin(), hiSec->relocs().end(), hiReloc,
+ [](const Relocation &lhs, const Relocation &rhs) {
+ return lhs.offset < rhs.offset;
+ });
+
+ for (auto it = range.first; it != range.second; ++it)
+ if (it->type == R_LARCH_PCADD_HI20 || it->type == R_LARCH_PCADD_GOT_HI20 ||
+ it->type == R_LARCH_PCADD_TLS_IE_HI20 ||
+ it->type == R_LARCH_PCADD_TLS_DESC_HI20)
+ return &*it;
+
+ Err(ctx) << loSec->getLocation(loReloc.offset)
+ << ": R_LARCH_PCADD_LO12 relocation points to "
+ << hiSec->getObjMsg(d->value)
+ << " without an associated R_LARCH_PCADD_HI20 relocation";
+ return nullptr;
+}
+
// A TLS symbol's virtual address is relative to the TLS segment. Add a
// target-specific adjustment to produce a thread-pointer-relative offset.
static int64_t getTlsTpOffset(Ctx &ctx, const Symbol &s) {
@@ -884,6 +939,11 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
return getRelocTargetVA(ctx, *hiRel, r.sym->getVA(ctx));
return 0;
}
+ case RE_LOONGARCH_PC_INDIRECT: {
+ if (const Relocation *hiRel = getLoongArchPCAddHi20(ctx, this, r))
+ return getRelocTargetVA(ctx, *hiRel, r.sym->getVA(ctx, a));
+ return 0;
+ }
case RE_LOONGARCH_PAGE_PC:
return getLoongArchPageDelta(r.sym->getVA(ctx, a), p, r.type);
case R_PC:
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 43f19186f0981..5b12f40c8d5fc 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -210,7 +210,7 @@ static bool isRelExpr(RelExpr expr) {
return oneof<R_PC, R_GOTREL, R_GOTPLTREL, RE_ARM_PCA, RE_MIPS_GOTREL,
RE_PPC64_CALL, RE_PPC64_RELAX_TOC, RE_AARCH64_PAGE_PC,
R_RELAX_GOT_PC, RE_RISCV_PC_INDIRECT, RE_PPC64_RELAX_GOT_PC,
- RE_LOONGARCH_PAGE_PC>(expr);
+ RE_LOONGARCH_PAGE_PC, RE_LOONGARCH_PC_INDIRECT>(expr);
}
static RelExpr toPlt(RelExpr expr) {
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index d2a77bc953109..3213c9387606e 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -129,6 +129,7 @@ enum RelExpr {
// also reused for TLS, making the semantics differ from other architectures.
RE_LOONGARCH_GOT,
RE_LOONGARCH_GOT_PAGE_PC,
+ RE_LOONGARCH_PC_INDIRECT,
RE_LOONGARCH_TLSGD_PAGE_PC,
RE_LOONGARCH_TLSDESC_PAGE_PC,
};
diff --git a/lld/test/ELF/loongarch-call30.s b/lld/test/ELF/loongarch-call30.s
new file mode 100644
index 0000000000000..907e8704e908b
--- /dev/null
+++ b/lld/test/ELF/loongarch-call30.s
@@ -0,0 +1,64 @@
+# REQUIRES: loongarch
+
+# RUN: rm -rf %t && split-file %s %t
+# RUN: llvm-mc --filetype=obj --triple=loongarch32-unknown-elf %t/a.s -o %t/a.o
+
+# RUN: ld.lld %t/a.o --section-start=.text=0x20010 --section-start=.sec.foo=0x21020 -o %t/exe1
+# RUN: llvm-objdump --no-show-raw-insn -d %t/exe1 | FileCheck --match-full-lines %s --check-prefix=EXE1
+## hi20 = target - pc >> 12 = 0x21020 - 0x20010 >> 12 = 1
+## lo12 = target - pc & (1 << 12) - 1 = 0x21020 - 0x20010 & 0xfff = 16
+# EXE1: 20010: pcaddu12i $t0, 1
+# EXE1-NEXT: 20014: jirl $zero, $t0, 16
+
+# RUN: ld.lld %t/a.o --section-start=.text=0x20010 --section-start=.sec.foo=0x21820 -o %t/exe2
+# RUN: llvm-objdump --no-show-raw-insn -d %t/exe2 | FileCheck --match-full-lines %s --check-prefix=EXE2
+## hi20 = target - pc >> 12 = 0x21820 - 0x20010 >> 12 = 1
+## lo12 = target - pc & (1 << 12) - 1 = 0x21820 - 0x20010 & 0xfff = 2064
+# EXE2: 20010: pcaddu12i $t0, 1
+# EXE2-NEXT: 20014: jirl $zero, $t0, 2064
+
+# RUN: ld.lld %t/a.o -shared -T %t/a.t -o %t/a.so
+# RUN: llvm-readelf -x .got.plt %t/a.so | FileCheck --check-prefix=GOTPLT %s
+# RUN: llvm-objdump -d --no-show-raw-insn %t/a.so | FileCheck --check-prefix=SO %s
+## PLT should be present in this case.
+# SO: Disassembly of section .plt:
+# SO: <.plt>:
+## foo@plt:
+# SO: 1234520: pcaddu12i $t3, 64{{$}}
+# SO-NEXT: ld.w $t3, $t3, 444{{$}}
+# SO-NEXT: jirl $t1, $t3, 0
+# SO-NEXT: nop
+
+# SO: Disassembly of section .text:
+# SO: <_start>:
+## hi20 = foo@plt - pc >> 12 = 0x1234520 - 0x1274670 >> 12 = -65
+## lo18 = foo@plt - pc & (1 << 12) - 1 = 0x1234520 - 0x1274670 & 0xfff = 3760
+# SO-NEXT: pcaddu12i $t0, -65{{$}}
+# SO-NEXT: jirl $zero, $t0, 3760{{$}}
+
+# GOTPLT: section '.got.plt':
+# GOTPLT-NEXT: 0x012746d4 00000000 00000000 00452301
+
+## Impossible case in reality becasue all LoongArch instructions are fixed 4-bytes long.
+# RUN: not ld.lld %t/a.o --section-start=.text=0x20000 --section-start=.sec.foo=0x40001 -o /dev/null 2>&1 | \
+# RUN: FileCheck -DFILE=%t/a.o --check-prefix=ERROR-ALIGN %s
+# ERROR-ALIGN: error: [[FILE]]:(.text+0x0): improper alignment for relocation R_LARCH_CALL30: 0x20001 is not aligned to 4 bytes
+
+#--- a.t
+SECTIONS {
+ .plt 0x1234500: { *(.plt) }
+ .text 0x1274670: { *(.text) }
+}
+
+#--- a.s
+.text
+.global _start
+_start:
+ .reloc ., R_LARCH_CALL30, foo
+ pcaddu12i $t0, 0
+ jirl $zero, $t0, 0
+
+.section .sec.foo,"awx"
+.global foo
+foo:
+ ret
diff --git a/lld/test/ELF/loongarch-relax-align.s b/lld/test/ELF/loongarch-relax-align.s
index 79353f2a3be47..b9da1322a8c00 100644
--- a/lld/test/ELF/loongarch-relax-align.s
+++ b/lld/test/ELF/loongarch-relax-align.s
@@ -1,7 +1,7 @@
# 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: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+32s,+relax %s -o %t.32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+32s,+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 --no-relax %t.32.o -o %t.32n
diff --git a/lld/test/ELF/loongarch-relax-emit-relocs.s b/lld/test/ELF/loongarch-relax-emit-relocs.s
index 909b65075a695..6e1e85c004439 100644
--- a/lld/test/ELF/loongarch-relax-emit-relocs.s
+++ b/lld/test/ELF/loongarch-relax-emit-relocs.s
@@ -1,7 +1,7 @@
# 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=loongarch32 --mattr=+32s,+relax %s -o %t.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax --defsym ELF64=1 %s -o %t.64.o
# 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
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
index f37de8e3b7c83..fe243397af346 100644
--- a/lld/test/ELF/loongarch-relax-pc-hi20-lo12-got-symbols.s
+++ b/lld/test/ELF/loongarch-relax-pc-hi20-lo12-got-symbols.s
@@ -4,9 +4,9 @@
# 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=loongarch32 -mattr=+32s,+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=loongarch32 -mattr=+32s,+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
diff --git a/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s b/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
index a417d89e9fa2e..a1d6c0f7eec73 100644
--- a/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
+++ b/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
@@ -1,6 +1,6 @@
# REQUIRES: loongarch
-# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax %s -o %t.32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+32s,+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
diff --git a/lld/test/ELF/loongarch-tls-gd-edge-case.s b/lld/test/ELF/loongarch-tls-gd-edge-case.s
index 9f25f10c73b44..cfa65f91aebb8 100644
--- a/lld/test/ELF/loongarch-tls-gd-edge-case.s
+++ b/lld/test/ELF/loongarch-tls-gd-edge-case.s
@@ -24,8 +24,8 @@
# LA64-REL-NEXT: 00000000000203a8 0000000200000009 R_LARCH_TLS_DTPREL64 0000000000000000 y + 0
# LA64-REL-NEXT: 00000000000203b0 000000020000000b R_LARCH_TLS_TPREL64 0000000000000000 y + 0
-# LA32: 101d4: pcalau12i $a0, 16
-# LA32-NEXT: ld.w $a0, $a0, 580
+# LA32: 101d4: pcaddu12i $a0, 16
+# LA32-NEXT: ld.w $a0, $a0, 112
# LA32-NEXT: pcalau12i $a1, 16
# LA32-NEXT: addi.w $a1, $a1, 572
diff --git a/lld/test/ELF/loongarch-tls-ie.s b/lld/test/ELF/loongarch-tls-ie.s
index ddfd9c976cb9b..84e6672e31e05 100644
--- a/lld/test/ELF/loongarch-tls-ie.s
+++ b/lld/test/ELF/loongarch-tls-ie.s
@@ -41,11 +41,11 @@
## LA32:
## &.got[0] - . = 0x20214 - 0x101a4: 0x10 pages, page offset 0x214
## &.got[1] - . = 0x20218 - 0x101b0: 0x10 pages, page offset 0x218
-# IE32: 101a4: pcalau12i $a4, 16
-# IE32-NEXT: ld.w $a4, $a4, 532
+# IE32: 101a4: pcaddu12i $a4, 16
+# IE32-NEXT: ld.w $a4, $a4, 112
# IE32-NEXT: add.w $a4, $a4, $tp
-# IE32-NEXT: 101b0: pcalau12i $a5, 16
-# IE32-NEXT: ld.w $a5, $a5, 536
+# IE32: 101b0: pcaddu12i $a5, 16
+# IE32-NEXT: ld.w $a5, $a5, 104
# IE32-NEXT: add.w $a5, $a5, $tp
## LA64:
@@ -62,15 +62,16 @@
# a@tprel = st_value(a) = 0x8
# b@tprel = st_value(a) = 0xc
-# LE32-GOT: could not find section '.got'
+# LE32-GOT: section '.got':
+# LE32-GOT-NEXT: 0x0003012c 08000000 0c000000
# LE64-GOT: could not find section '.got'
## LA32:
-# LE32: 200d4: nop
-# LE32-NEXT: ori $a4, $zero, 8
+# LE32: 20114: pcaddu12i $a4, 16
+# LE32-NEXT: ld.w $a4, $a4, 24
# LE32-NEXT: add.w $a4, $a4, $tp
-# LE32-NEXT: 200e0: nop
-# LE32-NEXT: ori $a5, $zero, 12
+# LE32: 20120: pcaddu12i $a5, 16
+# LE32-NEXT: ld.w $a5, $a5, 16
# LE32-NEXT: add.w $a5, $a5, $tp
## LA64:
diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def
index 4859057abcbb9..ee5f6ed8c89b4 100644
--- a/llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def
+++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def
@@ -149,3 +149,14 @@ ELF_RELOC(R_LARCH_TLS_LE_LO12_R, 123)
ELF_RELOC(R_LARCH_TLS_LD_PCREL20_S2, 124)
ELF_RELOC(R_LARCH_TLS_GD_PCREL20_S2, 125)
ELF_RELOC(R_LARCH_TLS_DESC_PCREL20_S2, 126)
+
+// Relocs added in ELF for the LoongArch™ Architecture v2025????, part of the
+// v2.40 LoongArch ABI specs.
+//
+// Spec addition: https://github.com/loongson/la-abi-specs/pull/16
+ELF_RELOC(R_LARCH_CALL30, 127)
+ELF_RELOC(R_LARCH_PCADD_HI20, 128)
+ELF_RELOC(R_LARCH_PCADD_GOT_HI20, 129)
+ELF_RELOC(R_LARCH_PCADD_TLS_IE_HI20, 130)
+ELF_RELOC(R_LARCH_PCADD_TLS_DESC_HI20, 131)
+ELF_RELOC(R_LARCH_PCADD_LO12_I, 132)
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
index 04c5a67ac4fe3..1d9038a6fe2d8 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
@@ -170,6 +170,30 @@ enum EdgeKind_loongarch : Edge::Kind {
///
PageOffset12,
+ /// The upper 20 bits of the offset from the fixup to the target.
+ ///
+ /// Fixup expression:
+ /// Fixup <- (Target + Addend - Fixup + 0x800) >> 12 : int20
+ ///
+ /// Notes:
+ /// For PCADDU12I fixups.
+ ///
+ /// Errors:
+ /// - The result of the fixup expression must fit into an int20 otherwise an
+ /// out-of-range error will be returned.
+ ///
+ PCAdd20,
+
+ /// The lower 12 bits of the offset from the paired PCADDU12I (the initial
+ /// target) to the final target it points to.
+ ///
+ /// Typically used to fix up ADDI/LD_W/LD_D immediates.
+ ///
+ /// Fixup expression:
+ /// Fixup <- (FinalTarget - InitialTarget) & 0xfff : int12
+ ///
+ PCAdd12,
+
/// A GOT entry getter/constructor, transformed to Page20 pointing at the GOT
/// entry for the original target.
///
@@ -206,6 +230,49 @@ enum EdgeKind_loongarch : Edge::Kind {
///
RequestGOTAndTransformToPageOffset12,
+ /// A GOT entry getter/constructor, transformed to PCAdd20 pointing at the GOT
+ /// entry for the original target.
+ ///
+ /// Indicates that this edge should be transformed into a PCAdd20 targeting
+ /// the GOT entry for the edge's current target, maintaining the same addend.
+ /// A GOT entry for the target should be created if one does not already
+ /// exist.
+ ///
+ /// Edges of this kind are usually handled by a GOT/PLT builder pass inserted
+ /// by default.
+ ///
+ /// Fixup expression:
+ /// NONE
+ ///
+ /// Errors:
+ /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
+ /// phase will result in an assert/unreachable during the fixup phase.
+ ///
+ RequestGOTAndTransformToPCAdd20,
+
+ /// A 30-bit PC-relative call.
+ ///
+ /// Represents a PC-relative call to a target within [-4G, +4G)
+ /// The target must be 4-byte aligned. For adjacent pcaddu12i+jirl
+ /// instruction pairs.
+ ///
+ /// Fixup expression:
+ /// Fixup <- (Target - Fixup + Addend) >> 2 : int30
+ ///
+ /// Notes:
+ /// The '30' in the name refers to the number operand bits and follows the
+ /// naming convention used by the corresponding ELF relocations. Since the low
+ /// two bits must be zero (because of the 4-byte alignment of the target) the
+ /// operand is effectively a signed 32-bit number.
+ ///
+ /// Errors:
+ /// - The result of the unshifted part of the fixup expression must be
+ /// 4-byte aligned otherwise an alignment error will be returned.
+ /// - The result of the fixup expression must fit into an int30 otherwise an
+ /// out-of-range error will be returned.
+ ///
+ Call30PCRel,
+
/// A 36-bit PC-relative call.
///
/// Represents a PC-relative call to a target within [-128G - 0x20000, +128G
...
[truncated]
|
@llvm/pr-subscribers-lld Author: hev (heiher) ChangesLink: loongson/la-abi-specs#16 Patch is 196.86 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/146499.diff 55 Files Affected:
diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index 3c4ad53af1b51..fa79c8df39f1b 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -161,6 +161,10 @@ static uint32_t setJ5(uint32_t insn, uint32_t imm) {
return (insn & 0xfffffc1f) | (extractBits(imm, 4, 0) << 5);
}
+static uint32_t setK10(uint32_t insn, uint32_t imm) {
+ return (insn & 0xffc003ff) | (extractBits(imm, 9, 0) << 10);
+}
+
static uint32_t setK12(uint32_t insn, uint32_t imm) {
return (insn & 0xffc003ff) | (extractBits(imm, 11, 0) << 10);
}
@@ -416,6 +420,8 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
// [1]: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=9f482b73f41a9a1bbfb173aad0733d1c824c788a
// [2]: https://github.com/loongson/la-abi-specs/pull/3
return isJirl(read32le(loc)) ? R_PLT : R_ABS;
+ case R_LARCH_PCADD_LO12_I:
+ return RE_LOONGARCH_PC_INDIRECT;
case R_LARCH_TLS_DTPREL32:
case R_LARCH_TLS_DTPREL64:
return R_DTPREL;
@@ -446,10 +452,12 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_32_PCREL:
case R_LARCH_64_PCREL:
case R_LARCH_PCREL20_S2:
+ case R_LARCH_PCADD_HI20:
return R_PC;
case R_LARCH_B16:
case R_LARCH_B21:
case R_LARCH_B26:
+ case R_LARCH_CALL30:
case R_LARCH_CALL36:
return R_PLT_PC;
case R_LARCH_GOT_PC_HI20:
@@ -459,6 +467,9 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_TLS_IE64_PC_LO20:
case R_LARCH_TLS_IE64_PC_HI12:
return RE_LOONGARCH_GOT_PAGE_PC;
+ case R_LARCH_PCADD_GOT_HI20:
+ case R_LARCH_PCADD_TLS_IE_HI20:
+ return R_GOT_PC;
case R_LARCH_GOT_PC_LO12:
case R_LARCH_TLS_IE_PC_LO12:
return RE_LOONGARCH_GOT;
@@ -522,6 +533,7 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_TLS_DESC_LO12:
case R_LARCH_TLS_DESC64_LO20:
case R_LARCH_TLS_DESC64_HI12:
+ case R_LARCH_PCADD_TLS_DESC_HI20:
return R_TLSDESC;
case R_LARCH_TLS_DESC_CALL:
return R_TLSDESC_CALL;
@@ -605,6 +617,22 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
write32le(loc, setD10k16(read32le(loc), val >> 2));
return;
+ case R_LARCH_CALL30: {
+ // This relocation is designed for adjacent pcaddu12i+jirl pairs that
+ // are patched in one time.
+ // The relocation range is [-4G, +4G) (of course must be 4-byte aligned).
+ if ((int64_t)val != llvm::SignExtend64(val, 32))
+ reportRangeError(ctx, loc, rel, Twine(val), llvm::minIntN(32),
+ llvm::maxIntN(32));
+ checkAlignment(ctx, loc, val, 4, rel);
+ uint32_t hi20 = extractBits(val, 31, 12);
+ // Despite the name, the lower part is actually 12 bits with 4-byte aligned.
+ uint32_t lo10 = extractBits(val, 11, 2);
+ write32le(loc, setJ20(read32le(loc), hi20));
+ write32le(loc + 4, setK10(read32le(loc + 4), lo10));
+ return;
+ }
+
case R_LARCH_CALL36: {
// This relocation is designed for adjacent pcaddu18i+jirl pairs that
// are patched in one time. Because of sign extension of these insns'
@@ -648,6 +676,7 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
case R_LARCH_TLS_LE_LO12_R:
case R_LARCH_TLS_DESC_PC_LO12:
case R_LARCH_TLS_DESC_LO12:
+ case R_LARCH_PCADD_LO12_I:
write32le(loc, setK12(read32le(loc), extractBits(val, 11, 0)));
return;
@@ -667,6 +696,15 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
case R_LARCH_TLS_DESC_HI20:
write32le(loc, setJ20(read32le(loc), extractBits(val, 31, 12)));
return;
+ case R_LARCH_PCADD_HI20:
+ case R_LARCH_PCADD_GOT_HI20:
+ case R_LARCH_PCADD_TLS_IE_HI20:
+ case R_LARCH_PCADD_TLS_DESC_HI20: {
+ uint64_t hi = val + 0x800;
+ checkInt(ctx, loc, SignExtend64(hi, 32) >> 12, 20, rel);
+ write32le(loc, setJ20(read32le(loc), extractBits(hi, 31, 12)));
+ return;
+ }
case R_LARCH_TLS_LE_HI20_R:
write32le(loc, setJ20(read32le(loc), extractBits(val + 0x800, 31, 12)));
return;
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index f8786265029e8..777209d5b20cf 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -713,6 +713,61 @@ static Relocation *getRISCVPCRelHi20(Ctx &ctx, const InputSectionBase *loSec,
return nullptr;
}
+// For RE_LARCH_PC_INDIRECT (R_LARCH_PCADD_LO12_I), the symbol actually
+// points the corresponding R_LARCH_PCADD_*_HI20 relocation, and the target VA
+// is calculated using PCADD_HI20's symbol.
+//
+// This function returns the R_LARCH_PCADD_*_HI20 relocation from the
+// R_LARCH_PCADD_LO12 relocation.
+static Relocation *getLoongArchPCAddHi20(Ctx &ctx,
+ const InputSectionBase *loSec,
+ const Relocation &loReloc) {
+ int64_t addend = loReloc.addend;
+ Symbol *sym = loReloc.sym;
+
+ const Defined *d = cast<Defined>(sym);
+ if (!d->section) {
+ Err(ctx) << loSec->getLocation(loReloc.offset)
+ << ": R_LARCH_PCADD_LO12 relocation points to an absolute symbol: "
+ << sym->getName();
+ return nullptr;
+ }
+ InputSection *hiSec = cast<InputSection>(d->section);
+
+ if (hiSec != loSec)
+ Err(ctx) << loSec->getLocation(loReloc.offset)
+ << ": R_LARCH_PCADD_LO12 relocation points to a symbol '"
+ << sym->getName() << "' in a different section '" << hiSec->name
+ << "'";
+
+ if (addend != 0)
+ Warn(ctx) << loSec->getLocation(loReloc.offset)
+ << ": non-zero addend in R_LARCH_PCADD_LO12 relocation to "
+ << hiSec->getObjMsg(d->value) << " is ignored";
+
+ // Relocations are sorted by offset, so we can use std::equal_range to do
+ // binary search.
+ Relocation hiReloc;
+ hiReloc.offset = d->value + addend;
+ auto range =
+ std::equal_range(hiSec->relocs().begin(), hiSec->relocs().end(), hiReloc,
+ [](const Relocation &lhs, const Relocation &rhs) {
+ return lhs.offset < rhs.offset;
+ });
+
+ for (auto it = range.first; it != range.second; ++it)
+ if (it->type == R_LARCH_PCADD_HI20 || it->type == R_LARCH_PCADD_GOT_HI20 ||
+ it->type == R_LARCH_PCADD_TLS_IE_HI20 ||
+ it->type == R_LARCH_PCADD_TLS_DESC_HI20)
+ return &*it;
+
+ Err(ctx) << loSec->getLocation(loReloc.offset)
+ << ": R_LARCH_PCADD_LO12 relocation points to "
+ << hiSec->getObjMsg(d->value)
+ << " without an associated R_LARCH_PCADD_HI20 relocation";
+ return nullptr;
+}
+
// A TLS symbol's virtual address is relative to the TLS segment. Add a
// target-specific adjustment to produce a thread-pointer-relative offset.
static int64_t getTlsTpOffset(Ctx &ctx, const Symbol &s) {
@@ -884,6 +939,11 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
return getRelocTargetVA(ctx, *hiRel, r.sym->getVA(ctx));
return 0;
}
+ case RE_LOONGARCH_PC_INDIRECT: {
+ if (const Relocation *hiRel = getLoongArchPCAddHi20(ctx, this, r))
+ return getRelocTargetVA(ctx, *hiRel, r.sym->getVA(ctx, a));
+ return 0;
+ }
case RE_LOONGARCH_PAGE_PC:
return getLoongArchPageDelta(r.sym->getVA(ctx, a), p, r.type);
case R_PC:
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 43f19186f0981..5b12f40c8d5fc 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -210,7 +210,7 @@ static bool isRelExpr(RelExpr expr) {
return oneof<R_PC, R_GOTREL, R_GOTPLTREL, RE_ARM_PCA, RE_MIPS_GOTREL,
RE_PPC64_CALL, RE_PPC64_RELAX_TOC, RE_AARCH64_PAGE_PC,
R_RELAX_GOT_PC, RE_RISCV_PC_INDIRECT, RE_PPC64_RELAX_GOT_PC,
- RE_LOONGARCH_PAGE_PC>(expr);
+ RE_LOONGARCH_PAGE_PC, RE_LOONGARCH_PC_INDIRECT>(expr);
}
static RelExpr toPlt(RelExpr expr) {
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index d2a77bc953109..3213c9387606e 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -129,6 +129,7 @@ enum RelExpr {
// also reused for TLS, making the semantics differ from other architectures.
RE_LOONGARCH_GOT,
RE_LOONGARCH_GOT_PAGE_PC,
+ RE_LOONGARCH_PC_INDIRECT,
RE_LOONGARCH_TLSGD_PAGE_PC,
RE_LOONGARCH_TLSDESC_PAGE_PC,
};
diff --git a/lld/test/ELF/loongarch-call30.s b/lld/test/ELF/loongarch-call30.s
new file mode 100644
index 0000000000000..907e8704e908b
--- /dev/null
+++ b/lld/test/ELF/loongarch-call30.s
@@ -0,0 +1,64 @@
+# REQUIRES: loongarch
+
+# RUN: rm -rf %t && split-file %s %t
+# RUN: llvm-mc --filetype=obj --triple=loongarch32-unknown-elf %t/a.s -o %t/a.o
+
+# RUN: ld.lld %t/a.o --section-start=.text=0x20010 --section-start=.sec.foo=0x21020 -o %t/exe1
+# RUN: llvm-objdump --no-show-raw-insn -d %t/exe1 | FileCheck --match-full-lines %s --check-prefix=EXE1
+## hi20 = target - pc >> 12 = 0x21020 - 0x20010 >> 12 = 1
+## lo12 = target - pc & (1 << 12) - 1 = 0x21020 - 0x20010 & 0xfff = 16
+# EXE1: 20010: pcaddu12i $t0, 1
+# EXE1-NEXT: 20014: jirl $zero, $t0, 16
+
+# RUN: ld.lld %t/a.o --section-start=.text=0x20010 --section-start=.sec.foo=0x21820 -o %t/exe2
+# RUN: llvm-objdump --no-show-raw-insn -d %t/exe2 | FileCheck --match-full-lines %s --check-prefix=EXE2
+## hi20 = target - pc >> 12 = 0x21820 - 0x20010 >> 12 = 1
+## lo12 = target - pc & (1 << 12) - 1 = 0x21820 - 0x20010 & 0xfff = 2064
+# EXE2: 20010: pcaddu12i $t0, 1
+# EXE2-NEXT: 20014: jirl $zero, $t0, 2064
+
+# RUN: ld.lld %t/a.o -shared -T %t/a.t -o %t/a.so
+# RUN: llvm-readelf -x .got.plt %t/a.so | FileCheck --check-prefix=GOTPLT %s
+# RUN: llvm-objdump -d --no-show-raw-insn %t/a.so | FileCheck --check-prefix=SO %s
+## PLT should be present in this case.
+# SO: Disassembly of section .plt:
+# SO: <.plt>:
+## foo@plt:
+# SO: 1234520: pcaddu12i $t3, 64{{$}}
+# SO-NEXT: ld.w $t3, $t3, 444{{$}}
+# SO-NEXT: jirl $t1, $t3, 0
+# SO-NEXT: nop
+
+# SO: Disassembly of section .text:
+# SO: <_start>:
+## hi20 = foo@plt - pc >> 12 = 0x1234520 - 0x1274670 >> 12 = -65
+## lo18 = foo@plt - pc & (1 << 12) - 1 = 0x1234520 - 0x1274670 & 0xfff = 3760
+# SO-NEXT: pcaddu12i $t0, -65{{$}}
+# SO-NEXT: jirl $zero, $t0, 3760{{$}}
+
+# GOTPLT: section '.got.plt':
+# GOTPLT-NEXT: 0x012746d4 00000000 00000000 00452301
+
+## Impossible case in reality becasue all LoongArch instructions are fixed 4-bytes long.
+# RUN: not ld.lld %t/a.o --section-start=.text=0x20000 --section-start=.sec.foo=0x40001 -o /dev/null 2>&1 | \
+# RUN: FileCheck -DFILE=%t/a.o --check-prefix=ERROR-ALIGN %s
+# ERROR-ALIGN: error: [[FILE]]:(.text+0x0): improper alignment for relocation R_LARCH_CALL30: 0x20001 is not aligned to 4 bytes
+
+#--- a.t
+SECTIONS {
+ .plt 0x1234500: { *(.plt) }
+ .text 0x1274670: { *(.text) }
+}
+
+#--- a.s
+.text
+.global _start
+_start:
+ .reloc ., R_LARCH_CALL30, foo
+ pcaddu12i $t0, 0
+ jirl $zero, $t0, 0
+
+.section .sec.foo,"awx"
+.global foo
+foo:
+ ret
diff --git a/lld/test/ELF/loongarch-relax-align.s b/lld/test/ELF/loongarch-relax-align.s
index 79353f2a3be47..b9da1322a8c00 100644
--- a/lld/test/ELF/loongarch-relax-align.s
+++ b/lld/test/ELF/loongarch-relax-align.s
@@ -1,7 +1,7 @@
# 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: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+32s,+relax %s -o %t.32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+32s,+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 --no-relax %t.32.o -o %t.32n
diff --git a/lld/test/ELF/loongarch-relax-emit-relocs.s b/lld/test/ELF/loongarch-relax-emit-relocs.s
index 909b65075a695..6e1e85c004439 100644
--- a/lld/test/ELF/loongarch-relax-emit-relocs.s
+++ b/lld/test/ELF/loongarch-relax-emit-relocs.s
@@ -1,7 +1,7 @@
# 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=loongarch32 --mattr=+32s,+relax %s -o %t.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax --defsym ELF64=1 %s -o %t.64.o
# 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
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
index f37de8e3b7c83..fe243397af346 100644
--- a/lld/test/ELF/loongarch-relax-pc-hi20-lo12-got-symbols.s
+++ b/lld/test/ELF/loongarch-relax-pc-hi20-lo12-got-symbols.s
@@ -4,9 +4,9 @@
# 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=loongarch32 -mattr=+32s,+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=loongarch32 -mattr=+32s,+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
diff --git a/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s b/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
index a417d89e9fa2e..a1d6c0f7eec73 100644
--- a/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
+++ b/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
@@ -1,6 +1,6 @@
# REQUIRES: loongarch
-# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax %s -o %t.32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+32s,+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
diff --git a/lld/test/ELF/loongarch-tls-gd-edge-case.s b/lld/test/ELF/loongarch-tls-gd-edge-case.s
index 9f25f10c73b44..cfa65f91aebb8 100644
--- a/lld/test/ELF/loongarch-tls-gd-edge-case.s
+++ b/lld/test/ELF/loongarch-tls-gd-edge-case.s
@@ -24,8 +24,8 @@
# LA64-REL-NEXT: 00000000000203a8 0000000200000009 R_LARCH_TLS_DTPREL64 0000000000000000 y + 0
# LA64-REL-NEXT: 00000000000203b0 000000020000000b R_LARCH_TLS_TPREL64 0000000000000000 y + 0
-# LA32: 101d4: pcalau12i $a0, 16
-# LA32-NEXT: ld.w $a0, $a0, 580
+# LA32: 101d4: pcaddu12i $a0, 16
+# LA32-NEXT: ld.w $a0, $a0, 112
# LA32-NEXT: pcalau12i $a1, 16
# LA32-NEXT: addi.w $a1, $a1, 572
diff --git a/lld/test/ELF/loongarch-tls-ie.s b/lld/test/ELF/loongarch-tls-ie.s
index ddfd9c976cb9b..84e6672e31e05 100644
--- a/lld/test/ELF/loongarch-tls-ie.s
+++ b/lld/test/ELF/loongarch-tls-ie.s
@@ -41,11 +41,11 @@
## LA32:
## &.got[0] - . = 0x20214 - 0x101a4: 0x10 pages, page offset 0x214
## &.got[1] - . = 0x20218 - 0x101b0: 0x10 pages, page offset 0x218
-# IE32: 101a4: pcalau12i $a4, 16
-# IE32-NEXT: ld.w $a4, $a4, 532
+# IE32: 101a4: pcaddu12i $a4, 16
+# IE32-NEXT: ld.w $a4, $a4, 112
# IE32-NEXT: add.w $a4, $a4, $tp
-# IE32-NEXT: 101b0: pcalau12i $a5, 16
-# IE32-NEXT: ld.w $a5, $a5, 536
+# IE32: 101b0: pcaddu12i $a5, 16
+# IE32-NEXT: ld.w $a5, $a5, 104
# IE32-NEXT: add.w $a5, $a5, $tp
## LA64:
@@ -62,15 +62,16 @@
# a@tprel = st_value(a) = 0x8
# b@tprel = st_value(a) = 0xc
-# LE32-GOT: could not find section '.got'
+# LE32-GOT: section '.got':
+# LE32-GOT-NEXT: 0x0003012c 08000000 0c000000
# LE64-GOT: could not find section '.got'
## LA32:
-# LE32: 200d4: nop
-# LE32-NEXT: ori $a4, $zero, 8
+# LE32: 20114: pcaddu12i $a4, 16
+# LE32-NEXT: ld.w $a4, $a4, 24
# LE32-NEXT: add.w $a4, $a4, $tp
-# LE32-NEXT: 200e0: nop
-# LE32-NEXT: ori $a5, $zero, 12
+# LE32: 20120: pcaddu12i $a5, 16
+# LE32-NEXT: ld.w $a5, $a5, 16
# LE32-NEXT: add.w $a5, $a5, $tp
## LA64:
diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def
index 4859057abcbb9..ee5f6ed8c89b4 100644
--- a/llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def
+++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def
@@ -149,3 +149,14 @@ ELF_RELOC(R_LARCH_TLS_LE_LO12_R, 123)
ELF_RELOC(R_LARCH_TLS_LD_PCREL20_S2, 124)
ELF_RELOC(R_LARCH_TLS_GD_PCREL20_S2, 125)
ELF_RELOC(R_LARCH_TLS_DESC_PCREL20_S2, 126)
+
+// Relocs added in ELF for the LoongArch™ Architecture v2025????, part of the
+// v2.40 LoongArch ABI specs.
+//
+// Spec addition: https://github.com/loongson/la-abi-specs/pull/16
+ELF_RELOC(R_LARCH_CALL30, 127)
+ELF_RELOC(R_LARCH_PCADD_HI20, 128)
+ELF_RELOC(R_LARCH_PCADD_GOT_HI20, 129)
+ELF_RELOC(R_LARCH_PCADD_TLS_IE_HI20, 130)
+ELF_RELOC(R_LARCH_PCADD_TLS_DESC_HI20, 131)
+ELF_RELOC(R_LARCH_PCADD_LO12_I, 132)
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
index 04c5a67ac4fe3..1d9038a6fe2d8 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
@@ -170,6 +170,30 @@ enum EdgeKind_loongarch : Edge::Kind {
///
PageOffset12,
+ /// The upper 20 bits of the offset from the fixup to the target.
+ ///
+ /// Fixup expression:
+ /// Fixup <- (Target + Addend - Fixup + 0x800) >> 12 : int20
+ ///
+ /// Notes:
+ /// For PCADDU12I fixups.
+ ///
+ /// Errors:
+ /// - The result of the fixup expression must fit into an int20 otherwise an
+ /// out-of-range error will be returned.
+ ///
+ PCAdd20,
+
+ /// The lower 12 bits of the offset from the paired PCADDU12I (the initial
+ /// target) to the final target it points to.
+ ///
+ /// Typically used to fix up ADDI/LD_W/LD_D immediates.
+ ///
+ /// Fixup expression:
+ /// Fixup <- (FinalTarget - InitialTarget) & 0xfff : int12
+ ///
+ PCAdd12,
+
/// A GOT entry getter/constructor, transformed to Page20 pointing at the GOT
/// entry for the original target.
///
@@ -206,6 +230,49 @@ enum EdgeKind_loongarch : Edge::Kind {
///
RequestGOTAndTransformToPageOffset12,
+ /// A GOT entry getter/constructor, transformed to PCAdd20 pointing at the GOT
+ /// entry for the original target.
+ ///
+ /// Indicates that this edge should be transformed into a PCAdd20 targeting
+ /// the GOT entry for the edge's current target, maintaining the same addend.
+ /// A GOT entry for the target should be created if one does not already
+ /// exist.
+ ///
+ /// Edges of this kind are usually handled by a GOT/PLT builder pass inserted
+ /// by default.
+ ///
+ /// Fixup expression:
+ /// NONE
+ ///
+ /// Errors:
+ /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
+ /// phase will result in an assert/unreachable during the fixup phase.
+ ///
+ RequestGOTAndTransformToPCAdd20,
+
+ /// A 30-bit PC-relative call.
+ ///
+ /// Represents a PC-relative call to a target within [-4G, +4G)
+ /// The target must be 4-byte aligned. For adjacent pcaddu12i+jirl
+ /// instruction pairs.
+ ///
+ /// Fixup expression:
+ /// Fixup <- (Target - Fixup + Addend) >> 2 : int30
+ ///
+ /// Notes:
+ /// The '30' in the name refers to the number operand bits and follows the
+ /// naming convention used by the corresponding ELF relocations. Since the low
+ /// two bits must be zero (because of the 4-byte alignment of the target) the
+ /// operand is effectively a signed 32-bit number.
+ ///
+ /// Errors:
+ /// - The result of the unshifted part of the fixup expression must be
+ /// 4-byte aligned otherwise an alignment error will be returned.
+ /// - The result of the fixup expression must fit into an int30 otherwise an
+ /// out-of-range error will be returned.
+ ///
+ Call30PCRel,
+
/// A 36-bit PC-relative call.
///
/// Represents a PC-relative call to a target within [-128G - 0x20000, +128G
...
[truncated]
|
Link: loongson/la-abi-specs#16
Link: https://sourceware.org/pipermail/binutils/2025-April/140730.html