[mips] Relax jalr/jr instructions using R_MIPS_JALR relocation
The R_MIPS_JALR relocation denotes jalr/jr instructions in position
independent code. Both these instructions take a target's address from
the $25 register. If offset to the target symbol fits into the 18-bits,
it's more efficient to replace jalr/jr by bal/b instructions.
Differential Revision: https://reviews.llvm.org/D68057
git-svn-id: https://llvm.org/svn/llvm-project/lld/trunk@372951 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/ELF/Arch/Mips.cpp b/ELF/Arch/Mips.cpp
index d218cf2..3674e1c 100644
--- a/ELF/Arch/Mips.cpp
+++ b/ELF/Arch/Mips.cpp
@@ -85,8 +85,14 @@
switch (type) {
case R_MIPS_JALR:
+ // If the target symbol is not preemptible and is not microMIPS,
+ // it might be possible to replace jalr/jr instruction by bal/b.
+ // It depends on the target symbol's offset.
+ if (!s.isPreemptible && !(s.getVA() & 0x1))
+ return R_PC;
+ return R_NONE;
case R_MICROMIPS_JALR:
- return R_HINT;
+ return R_NONE;
case R_MIPS_GPREL16:
case R_MIPS_GPREL32:
case R_MICROMIPS_GPREL16:
@@ -633,6 +639,20 @@
writeValue<e>(loc, val + 0x800080008000, 16, 48);
break;
case R_MIPS_JALR:
+ val -= 4;
+ // Replace jalr/jr instructions by bal/b if the target
+ // offset fits into the 18-bit range.
+ if (isInt<18>(val)) {
+ switch (read32<e>(loc)) {
+ case 0x0320f809: // jalr $25 => bal sym
+ write32<e>(loc, 0x04110000 | ((val >> 2) & 0xffff));
+ break;
+ case 0x03200008: // jr $25 => b sym
+ write32<e>(loc, 0x10000000 | ((val >> 2) & 0xffff));
+ break;
+ }
+ }
+ break;
case R_MICROMIPS_JALR:
// Ignore this optimization relocation for now
break;
diff --git a/test/ELF/mips-jalr.s b/test/ELF/mips-jalr.s
index a4a51ad..5b3d396 100644
--- a/test/ELF/mips-jalr.s
+++ b/test/ELF/mips-jalr.s
@@ -1,20 +1,56 @@
# REQUIRES: mips
-# Check that lld ignores R_MIPS_JALR relocation for now.
-# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
-# RUN: ld.lld %t.o -o %t.exe
+## Check handling of the R_MIPS_JALR relocation.
+
+# RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux %s -o %t.o
# RUN: llvm-readelf -r %t.o | FileCheck -check-prefix=REL %s
-# RUN: llvm-objdump -d --no-show-raw-insn %t.exe | FileCheck %s
-# REL: R_MIPS_CALL16 {{.*}} foo
+# RUN: ld.lld %t.o -shared -o %t.so
+# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck -check-prefix=SO %s
+
+# RUN: ld.lld %t.o --defsym=bar=__start -o %t.so
+# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck -check-prefix=EXE %s
+
+# REL: R_MIPS_JALR {{.*}} bar
# REL: R_MIPS_JALR {{.*}} foo
+# REL: R_MIPS_JALR {{.*}} far
-# CHECK: jalr $25
+# SO: jalr $25
+# SO: bal -24 <foo>
+# SO: jalr $25
+
+# SO: jr $25
+# SO: b -64 <foo>
+# SO: jr $25
+
+# EXE: bal -4 <foo>
+# EXE: bal -24 <foo>
+# EXE: jalr $25
+
+# EXE: b -56 <foo>
+# EXE: b -64 <foo>
+# EXE: jr $25
.text
- .global __start
+ .global bar
+ .global __start
.option pic2
+far:
+ .space 0x4fff0
__start:
- jal foo
foo:
+ jal bar
nop
+ jal foo
+ nop
+ jal far
+ nop
+l1:
+ jr $25
+ .reloc l1, R_MIPS_JALR, bar
+l2:
+ jr $25
+ .reloc l2, R_MIPS_JALR, foo
+l3:
+ jr $25
+ .reloc l3, R_MIPS_JALR, far