Skip to content
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

[LLD][RISCV] Add relaxation for absolute symbol whose hi20 can use c.lui #86294

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

preames
Copy link
Collaborator

@preames preames commented Mar 22, 2024

If we have an absolute address whose hi20 bits are known to be an int6, we can use a c.lui instead of a lui for the high bits. This does not reduce dynamic icount, but does reduce codesize by 2 bytes.

If we have an absolute address whose hi20 bits are known to be an int6,
we can use a c.lui instead of a lui for the high bits.  This does not
reduce dynamic icount, but does reduce codesize by 2 bytes.
@llvmbot
Copy link
Collaborator

llvmbot commented Mar 22, 2024

@llvm/pr-subscribers-lld

@llvm/pr-subscribers-lld-elf

Author: Philip Reames (preames)

Changes

If we have an absolute address whose hi20 bits are known to be an int6, we can use a c.lui instead of a lui for the high bits. This does not reduce dynamic icount, but does reduce codesize by 2 bytes.


Full diff: https://github.com/llvm/llvm-project/pull/86294.diff

2 Files Affected:

  • (modified) lld/ELF/Arch/RISCV.cpp (+34-17)
  • (added) lld/test/ELF/riscv-relax-hi20-lo12-rvc.s (+60)
diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 20de1b9b7bde96..9172349bc5ced2 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -70,7 +70,9 @@ enum Op {
 };
 
 enum Reg {
+  X_0 = 0,
   X_RA = 1,
+  X_2 = 2,
   X_GP = 3,
   X_TP = 4,
   X_T0 = 5,
@@ -789,25 +791,39 @@ static void relaxTlsLe(const InputSection &sec, size_t i, uint64_t loc,
 
 static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc,
                           Relocation &r, uint32_t &remove) {
-  const Defined *gp = ElfSym::riscvGlobalPointer;
-  if (!gp)
-    return;
-
-  if (!isInt<12>(r.sym->getVA(r.addend) - gp->getVA()))
+  if (const Defined *gp = ElfSym::riscvGlobalPointer;
+      gp && isInt<12>(r.sym->getVA(r.addend) - gp->getVA())) {
+    switch (r.type) {
+    case R_RISCV_RVC_LUI:
+      // Remove c.lui rd, %hi6(x).
+      sec.relaxAux->relocTypes[i] = R_RISCV_RELAX;
+      remove = 2;
+      break;
+    case R_RISCV_HI20:
+      // Remove lui rd, %hi20(x).
+      sec.relaxAux->relocTypes[i] = R_RISCV_RELAX;
+      remove = 4;
+      break;
+    case R_RISCV_LO12_I:
+      sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_I;
+      break;
+    case R_RISCV_LO12_S:
+      sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_S;
+      break;
+    }
     return;
+  }
 
-  switch (r.type) {
-  case R_RISCV_HI20:
-    // Remove lui rd, %hi20(x).
-    sec.relaxAux->relocTypes[i] = R_RISCV_RELAX;
-    remove = 4;
-    break;
-  case R_RISCV_LO12_I:
-    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_I;
-    break;
-  case R_RISCV_LO12_S:
-    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_S;
-    break;
+  // Use c.lui instead of lui if compressed and range allows
+  const bool rvc = getEFlags(sec.file) & EF_RISCV_RVC;
+  if (rvc && r.type == R_RISCV_HI20 && isInt<6>(hi20(r.sym->getVA(r.addend)))) {
+    if (uint32_t rd = (read32le(sec.content().data() + r.offset) >> 7) & 31;
+        rd != X_0 && rd != X_2) {
+      sec.relaxAux->relocTypes[i] = R_RISCV_RVC_LUI;
+      sec.relaxAux->writes.push_back(0x6001 | rd << 7); // c.lui
+      remove = 2;
+      return;
+    }
   }
 }
 
@@ -993,6 +1009,7 @@ void RISCV::finalizeRelax(int passes) const {
           case R_RISCV_RELAX:
             // Used by relaxTlsLe to indicate the relocation is ignored.
             break;
+          case R_RISCV_RVC_LUI:
           case R_RISCV_RVC_JUMP:
             skip = 2;
             write16le(p, aux.writes[writesIdx++]);
diff --git a/lld/test/ELF/riscv-relax-hi20-lo12-rvc.s b/lld/test/ELF/riscv-relax-hi20-lo12-rvc.s
new file mode 100644
index 00000000000000..9406e6be8fab68
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-hi20-lo12-rvc.s
@@ -0,0 +1,60 @@
+# REQUIRES: riscv
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax,+c a.s -o rv32.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax,+c a.s -o rv64.o
+
+# RUN: ld.lld rv32.o lds -o rv32
+# RUN: ld.lld rv64.o lds -o rv64
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32 | FileCheck %s
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
+
+# CHECK: 0000002c l       .text {{0*}}0 a
+
+# CHECK:      c.lui   a0, 0x1
+# CHECK-NEXT: addi    a0, a0, 0x0
+# CHECK-NEXT: lw      a0, 0x0(a0)
+# CHECK-NEXT: sw      a0, 0x0(a0)
+# CHECK-NEXT: c.lui   a0, 0x1f
+# CHECK-NEXT: addi    a0, a0, 0x7ff
+# CHECK-NEXT: lb      a0, 0x7ff(a0)
+# CHECK-NEXT: sb      a0, 0x7ff(a0)
+# CHECK-NEXT: lui     a0, 0x20
+# CHECK-NEXT: addi    a0, a0, -0x800
+# CHECK-NEXT: lw      a0, -0x800(a0)
+# CHECK-NEXT: sw      a0, -0x800(a0)
+# CHECK-EMPTY:
+# CHECK-NEXT: <a>:
+# CHECK-NEXT: c.addi a0, 0x1
+
+#--- a.s
+.global _start
+_start:
+  lui a0, %hi(rvc_lui_low)
+  addi a0, a0, %lo(rvc_lui_low)
+  lw a0, %lo(rvc_lui_low)(a0)
+  sw a0, %lo(rvc_lui_low)(a0)
+  lui a0, %hi(rvc_lui_high)
+  addi a0, a0, %lo(rvc_lui_high)
+  lb a0, %lo(rvc_lui_high)(a0)
+  sb a0, %lo(rvc_lui_high)(a0)
+  lui a0, %hi(norelax)
+  addi a0, a0, %lo(norelax)
+  lw a0, %lo(norelax)(a0)
+  sw a0, %lo(norelax)(a0)
+a:
+  addi a0, a0, 1
+
+.section .sdata,"aw"
+rvc_lui_low:
+  .space 124927
+rvc_lui_high:
+  .byte 0
+norelax:
+  .word 0
+
+#--- lds
+SECTIONS {
+  .text : {*(.text) }
+  .sdata 0x1000 : { }
+}

if (const Defined *gp = ElfSym::riscvGlobalPointer;
gp && isInt<12>(r.sym->getVA(r.addend) - gp->getVA())) {
switch (r.type) {
case R_RISCV_RVC_LUI:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the addition of the handling for R_RISCV_RVC_LUI is technically a separate change. Previously, if a user manually wrote a c.lui relocation in the assembler (clang doesn't seem to ever emit this), and we applied gp relaxation we'd end up with a stray c.lui whose result was never read. I noticed it while thinking through the interactions with the new code, but it could be separated if that was reviewer preference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants