[lld] [RISCV] Compress unrelaxable lui when RVC attribute is present (PR #74715)

Nemanja Ivanovic via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 11 07:47:33 PST 2023


https://github.com/nemanjai updated https://github.com/llvm/llvm-project/pull/74715

>From 058c918d265c724d77ed8bb0482eefcd6604bbab Mon Sep 17 00:00:00 2001
From: Nemanja Ivanovic <nemanja at synopsys.com>
Date: Thu, 7 Dec 2023 14:15:40 +0100
Subject: [PATCH 1/4] [RISCV] Compress unrelaxable lui when RVC attribute is
 present

If the object being linked allows compressed instructions,
relaxation is on and we are unable to relax the HI20
relocation, emit c.lui instead of lui if the immediate
fits.
---
 lld/ELF/Arch/RISCV.cpp               | 30 ++++++++++++++++++
 lld/test/ELF/riscv-clui-relax-hi20.s | 46 ++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+)
 create mode 100644 lld/test/ELF/riscv-clui-relax-hi20.s

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 898e3e45b9e724..eac835505bfcbf 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -650,8 +650,37 @@ static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc,
   if (!gp)
     return;
 
+  bool noRelaxation = false;
   if (!isInt<12>(r.sym->getVA(r.addend) - gp->getVA()))
+    noRelaxation = true;
+
+  if (noRelaxation) {
+    if (r.type != R_RISCV_HI20 || !(config->eflags & EF_RISCV_RVC) ||
+        !isInt<18>(r.sym->getVA(r.addend)))
+      return;
+    // We have a HI20 reloc that fits in 18 bits but not in 12 bits
+    // and compressed instructions are enabled.
+    // Try to compress the lui to a c.lui.
+    const unsigned bits = config->wordsize * 8;
+    uint32_t rd =
+      extractBits(read32le(sec.content().data() + r.offset), 11, 7);
+    uint32_t newInsn = 0x6001 | (rd << 7); // c.lui
+    int64_t imm = SignExtend64(r.sym->getVA(r.addend) + 0x800, bits) >> 12;
+
+    // The high immediate may be outside the range if the low needs to be
+    // negative. So the isInt<18> check above is not sufficient. Also,
+    // rd != 2 for c.lui.
+    if (rd == 2 || !isInt<6>(imm))
+      return;
+    if (imm == 0)
+      newInsn &= 0xDFFF;
+    uint16_t imm17 = extractBits(imm, 17, 17) << 12;
+    uint16_t imm16_12 = extractBits(imm, 16, 12) << 2;
+    remove = 2;
+    sec.relaxAux->relocTypes[i] = R_RISCV_RVC_LUI;
+    sec.relaxAux->writes.push_back(newInsn | imm17 | imm16_12);
     return;
+  }
 
   switch (r.type) {
   case R_RISCV_HI20:
@@ -830,6 +859,7 @@ void elf::riscvFinalizeRelax(int passes) {
           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-clui-relax-hi20.s b/lld/test/ELF/riscv-clui-relax-hi20.s
new file mode 100644
index 00000000000000..ba3d4ecb9a54e1
--- /dev/null
+++ b/lld/test/ELF/riscv-clui-relax-hi20.s
@@ -0,0 +1,46 @@
+# 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 --relax-gp --undefined=__global_pointer$ rv32.o lds -o rv32
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64.o lds -o rv64
+# RUN: llvm-objdump -td -M no-aliases rv32 | FileCheck %s
+# RUN: llvm-objdump -td -M no-aliases rv64 | FileCheck %s
+
+# CHECK: 00002000 l       .data  {{0+}} foo
+# CHECK: 00002000 l       .data  {{0+}} $d.2
+# CHECK: 00001800 g       .sdata {{0+}} __global_pointer$
+
+# CHECK: <_start>:
+# CHECK: 09 65         c.lui   a0, 2
+# CHECK: 13 05 05 00   addi    a0, a0, 0
+# CHECK: 37 05 02 00   lui   a0, 32
+# CHECK: 13 05 05 00   addi    a0, a0, 0
+
+
+#--- a.s
+.global _start
+_start:
+  lui a0, %hi(foo)
+  addi a0, a0, %lo(foo)
+  lui a0, %hi(bar)
+  addi a0, a0, %lo(bar)
+
+.section .sdata,"aw"
+  .zero 32
+.section .data,"aw"
+foo:
+  .word 0
+.section .far,"aw"
+bar:
+  .word 0
+
+#--- lds
+SECTIONS {
+  .text : {*(.text) }
+  .sdata 0x1000 : { }
+  .data 0x2000 : { }
+  .far 0x20000 : { }
+}

>From 664961880ae99b62c97752ae4a53c1bffc7ed024 Mon Sep 17 00:00:00 2001
From: Nemanja Ivanovic <nemanja at synopsys.com>
Date: Thu, 7 Dec 2023 14:29:01 +0100
Subject: [PATCH 2/4] Run clang-format

---
 lld/ELF/Arch/RISCV.cpp               |  3 +--
 lld/test/ELF/riscv-clui-relax-hi20.s | 12 ++++++------
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index eac835505bfcbf..ffe1ece274f660 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -662,8 +662,7 @@ static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc,
     // and compressed instructions are enabled.
     // Try to compress the lui to a c.lui.
     const unsigned bits = config->wordsize * 8;
-    uint32_t rd =
-      extractBits(read32le(sec.content().data() + r.offset), 11, 7);
+    uint32_t rd = extractBits(read32le(sec.content().data() + r.offset), 11, 7);
     uint32_t newInsn = 0x6001 | (rd << 7); // c.lui
     int64_t imm = SignExtend64(r.sym->getVA(r.addend) + 0x800, bits) >> 12;
 
diff --git a/lld/test/ELF/riscv-clui-relax-hi20.s b/lld/test/ELF/riscv-clui-relax-hi20.s
index ba3d4ecb9a54e1..ef6284fd116b76 100644
--- a/lld/test/ELF/riscv-clui-relax-hi20.s
+++ b/lld/test/ELF/riscv-clui-relax-hi20.s
@@ -9,15 +9,15 @@
 # RUN: llvm-objdump -td -M no-aliases rv32 | FileCheck %s
 # RUN: llvm-objdump -td -M no-aliases rv64 | FileCheck %s
 
-# CHECK: 00002000 l       .data  {{0+}} foo
-# CHECK: 00002000 l       .data  {{0+}} $d.2
+# CHECK: 00002004 l       .data  {{0+}} foo
+# CHECK: 00020004 l       .far   {{0+}} bar
 # CHECK: 00001800 g       .sdata {{0+}} __global_pointer$
 
 # CHECK: <_start>:
 # CHECK: 09 65         c.lui   a0, 2
-# CHECK: 13 05 05 00   addi    a0, a0, 0
+# CHECK: 13 05 45 00   addi    a0, a0, 4
 # CHECK: 37 05 02 00   lui   a0, 32
-# CHECK: 13 05 05 00   addi    a0, a0, 0
+# CHECK: 13 05 45 00   addi    a0, a0, 4
 
 
 #--- a.s
@@ -41,6 +41,6 @@ bar:
 SECTIONS {
   .text : {*(.text) }
   .sdata 0x1000 : { }
-  .data 0x2000 : { }
-  .far 0x20000 : { }
+  .data 0x2004 : { }
+  .far 0x20004 : { }
 }

>From 6cc3cee8465f7156ada7980736ca53e1b51b75a5 Mon Sep 17 00:00:00 2001
From: Nemanja Ivanovic <nemanja at synopsys.com>
Date: Mon, 11 Dec 2023 16:40:54 +0100
Subject: [PATCH 3/4] Address review comments

- Extract the correct bits from the immediate
- Add a test for an offset that fits in 18 bits, but the upper is not 6 bits
---
 lld/ELF/Arch/RISCV.cpp               | 4 ++--
 lld/test/ELF/riscv-clui-relax-hi20.s | 8 ++++++++
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index ffe1ece274f660..d1cb5b3b52f44f 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -673,8 +673,8 @@ static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc,
       return;
     if (imm == 0)
       newInsn &= 0xDFFF;
-    uint16_t imm17 = extractBits(imm, 17, 17) << 12;
-    uint16_t imm16_12 = extractBits(imm, 16, 12) << 2;
+    uint16_t imm17 = extractBits(imm, 5, 5) << 12;
+    uint16_t imm16_12 = extractBits(imm, 4, 0) << 2;
     remove = 2;
     sec.relaxAux->relocTypes[i] = R_RISCV_RVC_LUI;
     sec.relaxAux->writes.push_back(newInsn | imm17 | imm16_12);
diff --git a/lld/test/ELF/riscv-clui-relax-hi20.s b/lld/test/ELF/riscv-clui-relax-hi20.s
index ef6284fd116b76..4426b9d338603a 100644
--- a/lld/test/ELF/riscv-clui-relax-hi20.s
+++ b/lld/test/ELF/riscv-clui-relax-hi20.s
@@ -17,6 +17,8 @@
 # CHECK: 09 65         c.lui   a0, 2
 # CHECK: 13 05 45 00   addi    a0, a0, 4
 # CHECK: 37 05 02 00   lui   a0, 32
+# CHECK: 13 05 45 f8   addi    a0, a0, -124
+# CHECK: 37 05 02 00   lui   a0, 32
 # CHECK: 13 05 45 00   addi    a0, a0, 4
 
 
@@ -25,6 +27,8 @@
 _start:
   lui a0, %hi(foo)
   addi a0, a0, %lo(foo)
+  lui a0, %hi(neg)
+  addi a0, a0, %lo(neg)
   lui a0, %hi(bar)
   addi a0, a0, %lo(bar)
 
@@ -33,6 +37,9 @@ _start:
 .section .data,"aw"
 foo:
   .word 0
+.section .out,"aw"
+neg:
+  .word 0
 .section .far,"aw"
 bar:
   .word 0
@@ -42,5 +49,6 @@ SECTIONS {
   .text : {*(.text) }
   .sdata 0x1000 : { }
   .data 0x2004 : { }
+  .out 0x1FF84 : { }
   .far 0x20004 : { }
 }

>From 3b7d2e177a6fd92963a7b682cae179c3c452db91 Mon Sep 17 00:00:00 2001
From: Nemanja Ivanovic <nemanja at synopsys.com>
Date: Mon, 11 Dec 2023 16:47:03 +0100
Subject: [PATCH 4/4] Remove single-use bool and check eflags per-file

---
 lld/ELF/Arch/RISCV.cpp | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index d1cb5b3b52f44f..213a11948823d9 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -650,12 +650,8 @@ static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc,
   if (!gp)
     return;
 
-  bool noRelaxation = false;
-  if (!isInt<12>(r.sym->getVA(r.addend) - gp->getVA()))
-    noRelaxation = true;
-
-  if (noRelaxation) {
-    if (r.type != R_RISCV_HI20 || !(config->eflags & EF_RISCV_RVC) ||
+  if (!isInt<12>(r.sym->getVA(r.addend) - gp->getVA())) {
+    if (r.type != R_RISCV_HI20 || !(getEFlags(sec.file) & EF_RISCV_RVC) ||
         !isInt<18>(r.sym->getVA(r.addend)))
       return;
     // We have a HI20 reloc that fits in 18 bits but not in 12 bits



More information about the llvm-commits mailing list