[lld] 75e551e - [ELF] Relax R_RISCV_CALL and R_RISCV_CALL_PLT

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 7 10:18:49 PDT 2022


Author: Fangrui Song
Date: 2022-07-07T10:18:45-07:00
New Revision: 75e551e5d830754104f5cce8c7a97b69e56aac7d

URL: https://github.com/llvm/llvm-project/commit/75e551e5d830754104f5cce8c7a97b69e56aac7d
DIFF: https://github.com/llvm/llvm-project/commit/75e551e5d830754104f5cce8c7a97b69e56aac7d.diff

LOG: [ELF] Relax R_RISCV_CALL and R_RISCV_CALL_PLT

A pair of auipc+jalr relocated by R_RISCV_CALL or R_RISCV_CALL_PLT can be
converted to c.j, c.jal, or jal.

* c.j: RVC and displacement is representable as an int12
* c.jal: RV32C and displacement is representable as an int12
* jal: displacement is representable as an int21

Use the D127581 relaxation framework to implement the relaxation. If a shorter
sequence is satisfied, we record the new relocation type in `relocTypes` and
saves the new instruction into `writes`. Finally let `riscvFinalizeRelax` rewrite the
instruction by setting `skip`.

Differential Revision: https://reviews.llvm.org/D127611

Added: 
    lld/test/ELF/riscv-relax-call.s
    lld/test/ELF/riscv-relax-call2.s

Modified: 
    lld/ELF/Arch/RISCV.cpp

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 7d2255f44060..7031c65d37d1 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -270,11 +270,12 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
   case R_RISCV_TPREL_LO12_I:
   case R_RISCV_TPREL_LO12_S:
     return R_TPREL;
-  case R_RISCV_RELAX:
   case R_RISCV_TPREL_ADD:
     return R_NONE;
   case R_RISCV_ALIGN:
     return R_RELAX_HINT;
+  case R_RISCV_RELAX:
+    return config->relax ? R_RELAX_HINT : R_NONE;
   default:
     error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
           ") against symbol " + toString(s));
@@ -489,6 +490,9 @@ struct elf::RISCVRelaxAux {
   // For relocations[i], the actual offset is r_offset - (i ? relocDeltas[i-1] :
   // 0).
   std::unique_ptr<uint32_t[]> relocDeltas;
+  // For relocations[i], the actual type is relocTypes[i].
+  std::unique_ptr<RelType[]> relocTypes;
+  SmallVector<uint32_t, 0> writes;
 };
 
 static void initSymbolAnchors() {
@@ -498,9 +502,12 @@ static void initSymbolAnchors() {
       continue;
     for (InputSection *sec : getInputSections(*osec, storage)) {
       sec->relaxAux = make<RISCVRelaxAux>();
-      if (sec->relocations.size())
+      if (sec->relocations.size()) {
         sec->relaxAux->relocDeltas =
             std::make_unique<uint32_t[]>(sec->relocations.size());
+        sec->relaxAux->relocTypes =
+            std::make_unique<RelType[]>(sec->relocations.size());
+      }
     }
   }
   // Store anchors (st_value and st_value+st_size) for symbols relative to text
@@ -533,6 +540,33 @@ static void initSymbolAnchors() {
   }
 }
 
+// Relax R_RISCV_CALL/R_RISCV_CALL_PLT auipc+jalr to c.j, c.jal, or jal.
+static void relaxCall(const InputSection &sec, size_t i, uint64_t loc,
+                      Relocation &r, uint32_t &remove) {
+  const bool rvc = config->eflags & EF_RISCV_RVC;
+  const Symbol &sym = *r.sym;
+  const uint64_t insnPair = read64le(sec.rawData.data() + r.offset);
+  const uint32_t rd = extractBits(insnPair, 32 + 11, 32 + 7);
+  const uint64_t dest =
+      (r.expr == R_PLT_PC ? sym.getPltVA() : sym.getVA()) + r.addend;
+  const int64_t displace = dest - loc;
+
+  if (rvc && isInt<12>(displace) && rd == 0) {
+    sec.relaxAux->relocTypes[i] = R_RISCV_RVC_JUMP;
+    sec.relaxAux->writes.push_back(0xa001); // c.j
+    remove = 6;
+  } else if (rvc && isInt<12>(displace) && rd == X_RA &&
+             !config->is64) { // RV32C only
+    sec.relaxAux->relocTypes[i] = R_RISCV_RVC_JUMP;
+    sec.relaxAux->writes.push_back(0x2001); // c.jal
+    remove = 6;
+  } else if (isInt<21>(displace)) {
+    sec.relaxAux->relocTypes[i] = R_RISCV_JAL;
+    sec.relaxAux->writes.push_back(0x6f | rd << 7); // jal
+    remove = 4;
+  }
+}
+
 static bool relax(InputSection &sec) {
   const uint64_t secAddr = sec.getVA();
   auto &aux = *sec.relaxAux;
@@ -553,6 +587,8 @@ static bool relax(InputSection &sec) {
   sa = makeArrayRef(aux.anchors);
   delta = 0;
 
+  std::fill_n(aux.relocTypes.get(), sec.relocations.size(), R_RISCV_NONE);
+  aux.writes.clear();
   for (auto it : llvm::enumerate(sec.relocations)) {
     Relocation &r = it.value();
     const size_t i = it.index();
@@ -568,6 +604,12 @@ static bool relax(InputSection &sec) {
              "R_RISCV_ALIGN needs expanding the content");
       break;
     }
+    case R_RISCV_CALL:
+    case R_RISCV_CALL_PLT:
+      if (i + 1 != sec.relocations.size() &&
+          sec.relocations[i + 1].type == R_RISCV_RELAX)
+        relaxCall(sec, i, loc, r, remove);
+      break;
     }
 
     // For all anchors whose offsets are <= r.offset, they are preceded by
@@ -643,6 +685,7 @@ void elf::riscvFinalizeRelax(int passes) {
       ArrayRef<uint8_t> old = sec->rawData;
       size_t newSize =
           old.size() - aux.relocDeltas[sec->relocations.size() - 1];
+      size_t writesIdx = 0;
       uint8_t *p = context().bAlloc.Allocate<uint8_t>(newSize);
       uint64_t offset = 0;
       int64_t delta = 0;
@@ -679,6 +722,20 @@ void elf::riscvFinalizeRelax(int passes) {
               write16le(p + j, 0x0001); // c.nop
             }
           }
+        } else if (RelType newType = aux.relocTypes[i]) {
+          const uint32_t insn = aux.writes[writesIdx++];
+          switch (newType) {
+          case R_RISCV_RVC_JUMP:
+            skip = 2;
+            write16le(p, insn);
+            break;
+          case R_RISCV_JAL:
+            skip = 4;
+            write32le(p, insn);
+            break;
+          default:
+            llvm_unreachable("unsupported type");
+          }
         }
 
         p += skip;
@@ -694,6 +751,8 @@ void elf::riscvFinalizeRelax(int passes) {
         uint64_t cur = rels[i].offset;
         do {
           rels[i].offset -= delta;
+          if (aux.relocTypes[i] != R_RISCV_NONE)
+            rels[i].type = aux.relocTypes[i];
         } while (++i != e && rels[i].offset == cur);
         delta = aux.relocDeltas[i - 1];
       }

diff  --git a/lld/test/ELF/riscv-relax-call.s b/lld/test/ELF/riscv-relax-call.s
new file mode 100644
index 000000000000..c7a6461d2284
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-call.s
@@ -0,0 +1,160 @@
+# REQUIRES: riscv
+## Relax R_RISCV_CALL and R_RISCV_CALL_PLT.
+
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+## Without RVC
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax a.s -o a.32.o
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax b.s -o b.32.o
+# RUN: ld.lld -shared -soname=b.so b.32.o -o b.32.so
+# RUN: ld.lld -T lds a.32.o b.32.so -o 32
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 32 | FileCheck %s --check-prefix=NORVC
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax a.s -o a.64.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax b.s -o b.64.o
+# RUN: ld.lld -shared -soname=b.so b.64.o -o b.64.so
+# RUN: ld.lld -T lds a.64.o b.64.so -o 64
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64 | FileCheck %s --check-prefix=NORVC
+
+## RVC
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+c,+relax a.s -o a.32c.o
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+c,+relax b.s -o b.32c.o
+# RUN: ld.lld -shared -soname=b.so b.32c.o -o b.32c.so
+# RUN: ld.lld -T lds a.32c.o b.32c.so -o 32c
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 32c | FileCheck %s --check-prefixes=RVC,RVC32
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax a.s -o a.64c.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax b.s -o b.64c.o
+# RUN: ld.lld -shared -soname=b.so b.64c.o -o b.64c.so
+# RUN: ld.lld -T lds a.64c.o b.64c.so -o 64c
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64c | FileCheck %s --check-prefixes=RVC,RVC64
+
+## --no-relax disables relaxation.
+# RUN: ld.lld -T lds a.64.o b.64.so --no-relax -o 64.norelax
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64.norelax | FileCheck %s --check-prefixes=NORELAX
+
+# NORVC:       00010000 g       .text  {{0*}}0000001c _start
+# NORVC:       0001001c g       .text  {{0*}}00000000 _start_end
+# NORVC:       00010808 g       .mid   {{0*}}00000000 mid_end
+# NORVC:       00110016 g       .high  {{0*}}00000000 high_end
+
+# NORVC-LABEL: <_start>:
+# NORVC-NEXT:    10000:  jal    zero, {{.*}} <a>
+# NORVC-NEXT:            jal    zero, {{.*}} <a>
+# NORVC-NEXT:            addi   zero, zero, 0
+# NORVC-NEXT:            addi   zero, zero, 0
+# NORVC-NEXT:    10010:  jal    ra, {{.*}} <a>
+# NORVC-NEXT:            jal    ra, 0x10420
+# NORVC-EMPTY:
+
+# NORVC-LABEL: <.mid>:
+# NORVC-NEXT:    10800:  jal    ra, {{.*}} <_start>
+# NORVC-NEXT:            jal    ra, {{.*}} <_start>
+# NORVC-EMPTY:
+
+# NORVC-LABEL: <.mid2>:
+# NORVC-NEXT:    1080c:  jal    ra, {{.*}} <_start>
+# NORVC-EMPTY:
+
+# NORVC-LABEL: <.high>:
+# NORVC-NEXT:   110006:  auipc  ra, 1048320
+# NORVC-NEXT:            jalr   ra, -6(ra)
+# NORVC-NEXT:            auipc  ra, 1048320
+# NORVC-NEXT:            jalr   ra, -14(ra)
+# NORVC-EMPTY:
+
+# RVC32:       00010000 g       .text  00000016 _start
+# RVC32:       00010016 g       .text  00000000 _start_end
+# RVC32:       00010806 g       .mid   00000000 mid_end
+# RVC32:       0011000c g       .high  00000000 high_end
+# RVC64:       0000000000010000 g       .text  000000000000001a _start
+# RVC64:       000000000001001a g       .text  0000000000000000 _start_end
+# RVC64:       0000000000010808 g       .mid   0000000000000000 mid_end
+# RVC64:       0000000000110014 g       .high  0000000000000000 high_end
+
+# RVC-LABEL:   <_start>:
+# RVC-NEXT:      10000:  c.j    {{.*}} <a>
+# RVC-NEXT:              c.j    {{.*}} <a>
+# RVC-NEXT:              addi   zero, zero, 0
+# RVC-NEXT:              addi   zero, zero, 0
+# RVC-NEXT:              addi   zero, zero, 0
+# RVC32-NEXT:    10010:  c.jal  {{.*}} <a>
+# RVC32-NEXT:            c.jal  0x10420
+# RVC64-NEXT:    10010:  jal    ra, {{.*}} <a>
+# RVC64-NEXT:            jal    ra, 0x10420
+# RVC-EMPTY:
+# RVC-NEXT:    <a>:
+# RVC-NEXT:              c.jr   ra
+# RVC-EMPTY:
+
+# RVC-LABEL:   <.mid>:
+# RVC32-NEXT:    10800:  c.jal  {{.*}} <_start>
+# RVC64-NEXT:    10800:  jal    ra, {{.*}} <_start>
+# RVC-NEXT:              jal    ra, {{.*}} <_start>
+# RVC-EMPTY:
+
+# RVC-LABEL:   <.mid2>:
+# RVC32-NEXT:    1080a:  jal    ra, {{.*}} <_start>
+# RVC64-NEXT:    1080c:  jal    ra, {{.*}} <_start>
+# RVC-EMPTY:
+
+# RVC-LABEL:   <.high>:
+# RVC32-NEXT:   110000:  jal    ra, 0x10000 <_start>
+# RVC32-NEXT:            auipc  ra, 1048320
+# RVC32-NEXT:            jalr   ra, -4(ra)
+# RVC64-NEXT:   110004:  auipc  ra, 1048320
+# RVC64-NEXT:            jalr   ra, -4(ra)
+# RVC64-NEXT:            auipc  ra, 1048320
+# RVC64-NEXT:            jalr   ra, -12(ra)
+# RVC-EMPTY:
+
+# NORELAX-LABEL: <_start>:
+# NORELAX-NEXT:    10000:  auipc  t1, 0
+# NORELAX-NEXT:            jalr   zero, 32(t1)
+# NORELAX-NEXT:            auipc  t0, 0
+# NORELAX-NEXT:            jalr   zero, 24(t0)
+# NORELAX-NEXT:    10010:  auipc  ra, 0
+# NORELAX-NEXT:            jalr   ra, 16(ra)
+# NORELAX-NEXT:            auipc  ra, 0
+# NORELAX-NEXT:            jalr   ra, 1032(ra)
+# NORELAX-EMPTY:
+
+#--- a.s
+.global _start, _start_end
+_start:
+  tail a at plt
+  jump a, t0
+.balign 16
+  call a          # rv32c: c.jal; rv64c: jal
+  call bar        # PLT call can be relaxed. rv32c: c.jal; rv64c: jal
+
+a:
+  ret
+.size _start, . - _start
+_start_end:
+
+.section .mid,"ax", at progbits
+  call _start at plt # rv32c: c.jal; rv64c: jal
+  call _start at plt
+
+.section .mid2,"ax", at progbits
+  call _start at plt
+
+.section .high,"ax", at progbits
+  call _start at plt # relaxable for %t/32c
+  call _start at plt # not relaxed
+
+#--- b.s
+.globl bar
+bar:
+  ret
+
+#--- lds
+SECTIONS {
+  .text 0x10000 : { *(.text) }
+  .plt 0x10400 : { *(.plt) }
+  .mid 0x10800 : { *(.mid); mid_end = .; }
+  .mid2 mid_end+4 : { *(.mid2) }
+  # 22 is the size of _start in %t/32c (RVC32).
+  .high 0x110000+(_start_end-_start)-22 : { *(.high); high_end = .; }
+}

diff  --git a/lld/test/ELF/riscv-relax-call2.s b/lld/test/ELF/riscv-relax-call2.s
new file mode 100644
index 000000000000..03208ce8b547
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-call2.s
@@ -0,0 +1,58 @@
+# REQUIRES: riscv
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax a.s -o a.o
+
+# RUN: ld.lld -T lds a.o -o a
+# RUN: llvm-objdump -d --no-show-raw-insn -M no-aliases a | FileCheck %s
+
+## Unsure whether this needs a diagnostic. GNU ld allows this.
+# RUN: ld.lld -T lds -pie a.o -o a.pie
+# RUN: llvm-objdump -d --no-show-raw-insn -M no-aliases a.pie | FileCheck %s
+
+# RUN: ld.lld -T lds -pie -z notext -z ifunc-noplt a.o -o a.ifunc-noplt
+# RUN: llvm-objdump -d --no-show-raw-insn -M no-aliases a.ifunc-noplt | FileCheck %s --check-prefix=CHECK2
+
+# CHECK-LABEL:  <_start>:
+# CHECK-NEXT:             jal    zero, 0x8 <abs>
+# CHECK-NEXT:             jal    zero, 0x8 <abs>
+# CHECK-NEXT:             jal    ra, 0x8 <abs>
+# CHECK-NEXT:             auipc  t1, 1048320
+# CHECK-NEXT:             jalr   zero, -4(t1)
+# CHECK-EMPTY:
+
+# CHECK-LABEL:  <.mid>:
+# CHECK-NEXT:             jal    zero, 0x101000
+# CHECK-NEXT:             c.j    0x101000
+# CHECK-EMPTY:
+
+# CHECK2-LABEL: <.mid>:
+# CHECK2-NEXT:            auipc  t1, 0
+# CHECK2-NEXT:            jalr   zero, 0(t1)
+# CHECK2-NEXT:            auipc  t1, 0
+# CHECK2-NEXT:            jalr   zero, 0(t1)
+# CHECK2-EMPTY:
+
+#--- a.s
+.global _start, ifunc
+_start:
+  jump abs, t2
+  jump abs, t3
+  call abs
+  tail abs       # not relaxed
+
+.section .mid,"ax", at progbits
+.balign 16
+  tail ifunc at plt # not relaxed
+  tail ifunc at plt
+
+.type ifunc, @gnu_indirect_function
+ifunc:
+  ret
+
+#--- lds
+SECTIONS {
+  .text 0x100000 : { *(.text) }
+  .mid 0x100800 : { *(.mid) }
+  .iplt 0x101000 : { *(.iplt) }
+}
+abs = 8;


        


More information about the llvm-commits mailing list