[lld] [RISCV] Disable gp relaxation if part of object unreachable (PR #72655)

Nemanja Ivanovic via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 4 00:10:06 PST 2024


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

>From 61f44ad222d9379fab4722ce57aa94ce20b042e7 Mon Sep 17 00:00:00 2001
From: Nemanja Ivanovic <nemanja at synopsys.com>
Date: Fri, 17 Nov 2023 15:49:11 +0100
Subject: [PATCH 1/5] [RISCV] Disable gp relaxation if part of object
 unreachable

Linker gp relaxation is greedy. It will eliminate the LUI
with R_RISCV_HI20 if the base of the object is reachable
from the gp. The relaxation on the R_RISCV_LO12 will be
rejected if it is not reachable, but that is too late if
the corresponding R_RISCV_HI20 is gone.
This patch disables relaxation if the entire portion of
the object that can be relocated together is not reachable.

It is important that this does not necessarily mean the
size of the object since the size doesn't matter if its
alignment is smaller than its size.

In order to achieve correctness without excessively
pessimizing relaxation for large objects, relaxation
is rejected if the base of the object + min(s, ma) is
not reachable from gp, where:
s  - size of the object
ma - maximum alignment of the section that contains
     the object.
---
 lld/ELF/Arch/RISCV.cpp                | 14 +++++++
 lld/test/ELF/riscv-relax-gp-partial.s | 58 +++++++++++++++++++++++++++
 2 files changed, 72 insertions(+)
 create mode 100644 lld/test/ELF/riscv-relax-gp-partial.s

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 6413dcd7dcd797..d4c578934a0bb3 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -651,6 +651,20 @@ static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc,
   if (!isInt<12>(r.sym->getVA(r.addend) - gp->getVA()))
     return;
 
+  // The symbol may be accessed in multiple pieces. We need to make sure that
+  // all of the possible accesses are relaxed or none are. This prevents
+  // relaxing the hi relocation and being unable to relax one of the low
+  // relocations. The compiler will only access multiple pieces of an object
+  // with low relocations on the memory op if the alignment allows it.
+  // Therefore it should suffice to check that the smaller of the alignment
+  // and size can be reached from GP.
+  uint32_t alignAdjust =
+      r.sym->getOutputSection() ? r.sym->getOutputSection()->addralign : 0;
+  alignAdjust = std::min<uint32_t>(alignAdjust, r.sym->getSize());
+
+  if (!isInt<12>(r.sym->getVA() + alignAdjust - gp->getVA()))
+    return;
+
   switch (r.type) {
   case R_RISCV_HI20:
     // Remove lui rd, %hi20(x).
diff --git a/lld/test/ELF/riscv-relax-gp-partial.s b/lld/test/ELF/riscv-relax-gp-partial.s
new file mode 100644
index 00000000000000..79984d9b65b7cf
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-gp-partial.s
@@ -0,0 +1,58 @@
+# REQUIRES: riscv
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax 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 --no-show-raw-insn rv32 | FileCheck %s
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
+ 
+# CHECK: 00000080 l       .data  {{0+}}08 Var0
+# CHECK: 00001000 l       .data  {{0+}}80 Var1
+# CHECK: 00000815 g       .sdata {{0+}}00 __global_pointer$
+
+# CHECK: <_start>:
+# CHECK-NOT:  lui
+# CHECK-NEXT: lw      a0, -1941(gp)
+# CHECK-NEXT: lw      a1, -1937(gp)
+# CHECK-NEXT: lui     a1, 1
+# CHECK-NEXT: lw      a0, 0(a1)
+# CHECK-NEXT: lw      a1, 124(a1)
+
+#--- a.s
+.global _start
+_start:
+        lui     a1, %hi(Var0)
+        lw      a0, %lo(Var0)(a1)
+        lw      a1, %lo(Var0+4)(a1)
+        lui     a1, %hi(Var1)
+        lw      a0, %lo(Var1)(a1)      # First part is reachable from gp
+        lw      a1, %lo(Var1+124)(a1)  # The second part is not reachable
+
+.section .rodata
+foo:
+  .space 1
+.section .sdata,"aw"
+  .space 1
+.section .data,"aw"
+  .p2align 3
+Var0:
+  .quad 0
+  .size   Var0, 8
+  .space 3960
+  .p2align 7
+Var1:
+  .quad 0
+  .zero 120
+  .size   Var1, 128
+
+#--- lds
+    SECTIONS {
+        .text : { }
+        .rodata : { }
+        .sdata : { }
+        .sbss : { }
+        .data : { }
+    }

>From 0a64d43e3cc472a78021b278e8bf9c3cdba57ae4 Mon Sep 17 00:00:00 2001
From: Nemanja Ivanovic <nemanja at synopsys.com>
Date: Mon, 27 Nov 2023 16:24:40 +0100
Subject: [PATCH 2/5] Address review comments

- Add a test that rejects the relaxation due to alignment (smaller
  than object size)
- Fix the adjustment to be 1 byte less since that is the minimum
  requirement for addressability
- Add code to reject relaxing the HI20 relocation when the addend
  is non-zero (i.e. check the range HI20 +/- adjustment)
- Restrict the code that rejects relaxation to the relaxation of
  HI20 since the LO12 can still proceed as nothing depends on it
---
 lld/ELF/Arch/RISCV.cpp                        | 35 ++++++++----
 .../ELF/riscv-relax-gp-partial-align-min.s    | 40 ++++++++++++++
 .../ELF/riscv-relax-gp-partial-hi20-addend.s  | 54 +++++++++++++++++++
 lld/test/ELF/riscv-relax-gp-partial.s         | 16 +++---
 4 files changed, 126 insertions(+), 19 deletions(-)
 create mode 100644 lld/test/ELF/riscv-relax-gp-partial-align-min.s
 create mode 100644 lld/test/ELF/riscv-relax-gp-partial-hi20-addend.s

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index d4c578934a0bb3..7d01e7f89cd058 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -651,26 +651,39 @@ static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc,
   if (!isInt<12>(r.sym->getVA(r.addend) - gp->getVA()))
     return;
 
-  // The symbol may be accessed in multiple pieces. We need to make sure that
-  // all of the possible accesses are relaxed or none are. This prevents
-  // relaxing the hi relocation and being unable to relax one of the low
-  // relocations. The compiler will only access multiple pieces of an object
-  // with low relocations on the memory op if the alignment allows it.
-  // Therefore it should suffice to check that the smaller of the alignment
-  // and size can be reached from GP.
+  // The symbol may be accessed in multiple pieces with different addends.
+  // If we are relaxing the HI20 relocation, we need to ensure that we only
+  // relax (and delete the instruction) if all possible LO12 relocations
+  // that depend on it will be relaxable. The compiler will only access multiple
+  // pieces of an object with low relocations on the memory op if the alignment
+  // allows it. Therefore it should suffice to check that the smaller of the
+  // alignment and size can be reached from GP.
   uint32_t alignAdjust =
       r.sym->getOutputSection() ? r.sym->getOutputSection()->addralign : 0;
   alignAdjust = std::min<uint32_t>(alignAdjust, r.sym->getSize());
-
-  if (!isInt<12>(r.sym->getVA() + alignAdjust - gp->getVA()))
-    return;
+  if (alignAdjust)
+    alignAdjust--;
 
   switch (r.type) {
-  case R_RISCV_HI20:
+  case R_RISCV_HI20: {
+    uint64_t hiAddr = r.sym->getVA(r.addend);
+    // If the addend is zero, the LO12 relocations can only be accessing the
+    // range [base, base+alignAdjust] (where base == r.sym->getVA()).
+    if (r.addend == 0 && !isInt<12>(hiAddr + alignAdjust - gp->getVA()))
+      return;
+
+    // However, if the addend is non-zero, the LO12 relocations may be accessing
+    // the range [HI-alignAdjust-1, HI+alignAdjust].
+    if (r.addend != 0 &&
+        (!isInt<12>(hiAddr - alignAdjust - 1 - gp->getVA()) ||
+         !isInt<12>(hiAddr + alignAdjust - gp->getVA())))
+      return;
+
     // 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;
diff --git a/lld/test/ELF/riscv-relax-gp-partial-align-min.s b/lld/test/ELF/riscv-relax-gp-partial-align-min.s
new file mode 100644
index 00000000000000..0ffa315991cb82
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-gp-partial-align-min.s
@@ -0,0 +1,40 @@
+# REQUIRES: riscv
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax 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 --no-show-raw-insn rv32 | FileCheck %s
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
+ 
+# CHECK: 000017e0 l       .data  {{0+}}80 Var1
+# CHECK: 00000ffc g       .sdata {{0+}}00 __global_pointer$
+
+# CHECK: <_start>:
+# CHECK-NEXT: lui     a1, 1
+# CHECK-NEXT: lw      a0, 2020(gp)
+# CHECK-NEXT: lw      a1, 2044(a1)
+
+#--- a.s
+.global _start
+_start:
+        lui     a1, %hi(Var1)
+        lw      a0, %lo(Var1)(a1)      # First part is reachable from gp
+        lw      a1, %lo(Var1+28)(a1)   # The second part is not reachable
+
+.section .sdata,"aw"
+.section .data,"aw"
+  .p2align 5
+Var1:
+  .quad 0
+  .zero 120
+  .size   Var1, 128
+
+#--- lds
+SECTIONS {
+  .text : { }
+  .sdata 0x07fc : { }
+  .data  0x17E0 : { }
+}
diff --git a/lld/test/ELF/riscv-relax-gp-partial-hi20-addend.s b/lld/test/ELF/riscv-relax-gp-partial-hi20-addend.s
new file mode 100644
index 00000000000000..9877ec8137e4ec
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-gp-partial-hi20-addend.s
@@ -0,0 +1,54 @@
+# REQUIRES: riscv
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax 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 --no-show-raw-insn rv32 | FileCheck %s
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
+ 
+# CHECK: 00001000 l       .data  {{0+}}08 Var0
+# CHECK: 00001f80 l       .data1 {{0+}}80 Var1
+# CHECK: 00001800 g       .sdata {{0+}}00 __global_pointer$
+
+# CHECK: <_start>:
+# CHECK-NEXT: lui     a1, 1
+# CHECK-NEXT: lw      a0, -2048(gp)
+# CHECK-NEXT: lw      a1, -2044(gp)
+# CHECK-NEXT: lui     a1, 2
+# CHECK-NEXT: lw      a0, 1920(gp)
+# CHECK-NEXT: lw      a1, 2044(gp)
+
+#--- a.s
+.global _start
+_start:
+        lui     a1, %hi(Var0+4)        # Cannot prove that %lo relocs will be reachable
+        lw      a0, %lo(Var0)(a1)      # Reachable from GP
+        lw      a1, %lo(Var0+4)(a1)    # Reachable from GP
+        lui     a1, %hi(Var1+124)      # Cannot prove that %lo relocs will be reachable
+        lw      a0, %lo(Var1)(a1)      # Reachable from GP
+        lw      a1, %lo(Var1+124)(a1)  # Reachable from GP
+
+.section .sdata,"aw"
+.section .data,"aw"
+  .p2align 3
+Var0:
+  .quad 0
+  .size   Var0, 8
+
+.section .data1,"aw"
+  .p2align 7
+Var1:
+  .quad 0
+  .zero 120
+  .size   Var1, 128
+
+#--- lds
+SECTIONS {
+  .text : { }
+  .sdata 0x1000 : { }
+  .data  0x1000 : { }
+  .data1 0x1f80 : { }
+}
diff --git a/lld/test/ELF/riscv-relax-gp-partial.s b/lld/test/ELF/riscv-relax-gp-partial.s
index 79984d9b65b7cf..fedc41f24e2d6a 100644
--- a/lld/test/ELF/riscv-relax-gp-partial.s
+++ b/lld/test/ELF/riscv-relax-gp-partial.s
@@ -18,7 +18,7 @@
 # CHECK-NEXT: lw      a0, -1941(gp)
 # CHECK-NEXT: lw      a1, -1937(gp)
 # CHECK-NEXT: lui     a1, 1
-# CHECK-NEXT: lw      a0, 0(a1)
+# CHECK-NEXT: lw      a0, 2027(gp)
 # CHECK-NEXT: lw      a1, 124(a1)
 
 #--- a.s
@@ -49,10 +49,10 @@ Var1:
   .size   Var1, 128
 
 #--- lds
-    SECTIONS {
-        .text : { }
-        .rodata : { }
-        .sdata : { }
-        .sbss : { }
-        .data : { }
-    }
+SECTIONS {
+  .text : { }
+  .rodata : { }
+  .sdata : { }
+  .sbss : { }
+  .data : { }
+}

>From b8bd95f9c5590701b371d7d25996efe6d8289308 Mon Sep 17 00:00:00 2001
From: Nemanja Ivanovic <nemanja at synopsys.com>
Date: Mon, 27 Nov 2023 16:31:41 +0100
Subject: [PATCH 3/5] Run clang-format.

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

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 7d01e7f89cd058..ecbf8aaeeb3a51 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -674,9 +674,8 @@ static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc,
 
     // However, if the addend is non-zero, the LO12 relocations may be accessing
     // the range [HI-alignAdjust-1, HI+alignAdjust].
-    if (r.addend != 0 &&
-        (!isInt<12>(hiAddr - alignAdjust - 1 - gp->getVA()) ||
-         !isInt<12>(hiAddr + alignAdjust - gp->getVA())))
+    if (r.addend != 0 && (!isInt<12>(hiAddr - alignAdjust - 1 - gp->getVA()) ||
+                          !isInt<12>(hiAddr + alignAdjust - gp->getVA())))
       return;
 
     // Remove lui rd, %hi20(x).

>From c4667aa586e71a66efe086492e4f11766c9e2ef9 Mon Sep 17 00:00:00 2001
From: Nemanja Ivanovic <nemanja at synopsys.com>
Date: Mon, 27 Nov 2023 18:03:28 +0100
Subject: [PATCH 4/5] Consolidate tests into a single file.

---
 lld/test/ELF/riscv-relax-gp-edges.s           | 147 ++++++++++++++++++
 .../ELF/riscv-relax-gp-partial-align-min.s    |  40 -----
 .../ELF/riscv-relax-gp-partial-hi20-addend.s  |  54 -------
 lld/test/ELF/riscv-relax-gp-partial.s         |  58 -------
 4 files changed, 147 insertions(+), 152 deletions(-)
 create mode 100644 lld/test/ELF/riscv-relax-gp-edges.s
 delete mode 100644 lld/test/ELF/riscv-relax-gp-partial-align-min.s
 delete mode 100644 lld/test/ELF/riscv-relax-gp-partial-hi20-addend.s
 delete mode 100644 lld/test/ELF/riscv-relax-gp-partial.s

diff --git a/lld/test/ELF/riscv-relax-gp-edges.s b/lld/test/ELF/riscv-relax-gp-edges.s
new file mode 100644
index 00000000000000..e355e242732907
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-gp-edges.s
@@ -0,0 +1,147 @@
+# REQUIRES: riscv
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32a.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax a.s -o rv64a.o
+# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax b.s -o rv32b.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax b.s -o rv64b.o
+# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax c.s -o rv32c.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax c.s -o rv64c.o
+
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32a.o lds.a -o rv32a
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64a.o lds.a -o rv64a
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32b.o lds.b -o rv32b
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64b.o lds.b -o rv64b
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32c.o lds.c -o rv32c
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64c.o lds.c -o rv64c
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32a | FileCheck %s --check-prefix=CHECK-ALIGN
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64a | FileCheck %s --check-prefix=CHECK-ALIGN
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32b | FileCheck %s --check-prefix=CHECK-HI-ADD
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64b | FileCheck %s --check-prefix=CHECK-HI-ADD
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32c | FileCheck %s --check-prefix=CHECK-SIZE
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64c | FileCheck %s --check-prefix=CHECK-SIZE
+ 
+# CHECK-ALIGN: 000017e0 l       .data  {{0+}}80 Var1
+# CHECK-ALIGN: 00000ffc g       .sdata {{0+}}00 __global_pointer$
+
+# CHECK-ALIGN: <_start>:
+# CHECK-ALIGN-NEXT: lui     a1, 1
+# CHECK-ALIGN-NEXT: lw      a0, 2020(gp)
+# CHECK-ALIGN-NEXT: lw      a1, 2044(a1)
+
+#--- a.s
+# The relaxation on the lui is rejected because the alignment (which is smaller
+# than the size) doesn't allow it.
+.global _start
+_start:
+        lui     a1, %hi(Var1)
+        lw      a0, %lo(Var1)(a1)      # First part is reachable from gp
+        lw      a1, %lo(Var1+28)(a1)   # The second part is not reachable
+
+.section .sdata,"aw"
+.section .data,"aw"
+  .p2align 5
+Var1:
+  .quad 0
+  .zero 120
+  .size   Var1, 128
+
+#--- lds.a
+SECTIONS {
+  .text : { }
+  .sdata 0x07fc : { }
+  .data  0x17E0 : { }
+}
+ 
+# CHECK-HI-ADD: 00001000 l       .data  {{0+}}08 Var0
+# CHECK-HI-ADD: 00001f80 l       .data1 {{0+}}80 Var1
+# CHECK-HI-ADD: 00001800 g       .sdata {{0+}}00 __global_pointer$
+
+# CHECK-HI-ADD: <_start>:
+# CHECK-HI-ADD-NEXT: lui     a1, 1
+# CHECK-HI-ADD-NEXT: lw      a0, -2048(gp)
+# CHECK-HI-ADD-NEXT: lw      a1, -2044(gp)
+# CHECK-HI-ADD-NEXT: lui     a1, 2
+# CHECK-HI-ADD-NEXT: lw      a0, 1920(gp)
+# CHECK-HI-ADD-NEXT: lw      a1, 2044(gp)
+
+#--- b.s
+# The relaxation on the two lui are rejected because the amount of data a LO12
+# reloc is allowed to address below and above the respective HI20 goes past
+# the amount reachable from GP.
+.global _start
+_start:
+        lui     a1, %hi(Var0+4)        # Cannot prove that %lo relocs will be reachable
+        lw      a0, %lo(Var0)(a1)      # Reachable from GP
+        lw      a1, %lo(Var0+4)(a1)    # Reachable from GP
+        lui     a1, %hi(Var1+124)      # Cannot prove that %lo relocs will be reachable
+        lw      a0, %lo(Var1)(a1)      # Reachable from GP
+        lw      a1, %lo(Var1+124)(a1)  # Reachable from GP
+
+.section .sdata,"aw"
+.section .data,"aw"
+  .p2align 3
+Var0:
+  .quad 0
+  .size   Var0, 8
+
+.section .data1,"aw"
+  .p2align 7
+Var1:
+  .quad 0
+  .zero 120
+  .size   Var1, 128
+
+#--- lds.b
+SECTIONS {
+  .text : { }
+  .sdata 0x1000 : { }
+  .data  0x1000 : { }
+  .data1 0x1f80 : { }
+}
+
+# CHECK-SIZE: 00000080 l       .data  {{0+}}08 Var0
+# CHECK-SIZE: 00001000 l       .data1 {{0+}}80 Var1
+# CHECK-SIZE: 00000815 g       .sdata {{0+}}00 __global_pointer$
+
+# CHECK-SIZE: <_start>:
+# CHECK-SIZE-NOT:  lui
+# CHECK-SIZE-NEXT: lw      a0, -1941(gp)
+# CHECK-SIZE-NEXT: lw      a1, -1937(gp)
+# CHECK-SIZE-NEXT: lui     a1, 1
+# CHECK-SIZE-NEXT: lw      a0, 2027(gp)
+# CHECK-SIZE-NEXT: lw      a1, 124(a1)
+
+#--- c.s
+# The relaxation on the second lui is rejected because the size (and alignment)
+# allow for a LO12 that cannot reach its target from GP.
+.global _start
+_start:
+        lui     a1, %hi(Var0)
+        lw      a0, %lo(Var0)(a1)
+        lw      a1, %lo(Var0+4)(a1)
+        lui     a1, %hi(Var1)
+        lw      a0, %lo(Var1)(a1)      # First part is reachable from gp
+        lw      a1, %lo(Var1+124)(a1)  # The second part is not reachable
+
+.section .sdata,"aw"
+.section .data,"aw"
+  .p2align 3
+Var0:
+  .quad 0
+  .size   Var0, 8
+
+.section .data1,"aw"
+  .p2align 7
+Var1:
+  .quad 0
+  .zero 120
+  .size   Var1, 128
+
+#--- lds.c
+SECTIONS {
+  .text : { }
+  .sdata  0x0015 : { }
+  .data   0x0080 : { }
+  .data1  0x1000 : { }
+}
diff --git a/lld/test/ELF/riscv-relax-gp-partial-align-min.s b/lld/test/ELF/riscv-relax-gp-partial-align-min.s
deleted file mode 100644
index 0ffa315991cb82..00000000000000
--- a/lld/test/ELF/riscv-relax-gp-partial-align-min.s
+++ /dev/null
@@ -1,40 +0,0 @@
-# REQUIRES: riscv
-# RUN: rm -rf %t && split-file %s %t && cd %t
-
-# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o
-# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax 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 --no-show-raw-insn rv32 | FileCheck %s
-# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
- 
-# CHECK: 000017e0 l       .data  {{0+}}80 Var1
-# CHECK: 00000ffc g       .sdata {{0+}}00 __global_pointer$
-
-# CHECK: <_start>:
-# CHECK-NEXT: lui     a1, 1
-# CHECK-NEXT: lw      a0, 2020(gp)
-# CHECK-NEXT: lw      a1, 2044(a1)
-
-#--- a.s
-.global _start
-_start:
-        lui     a1, %hi(Var1)
-        lw      a0, %lo(Var1)(a1)      # First part is reachable from gp
-        lw      a1, %lo(Var1+28)(a1)   # The second part is not reachable
-
-.section .sdata,"aw"
-.section .data,"aw"
-  .p2align 5
-Var1:
-  .quad 0
-  .zero 120
-  .size   Var1, 128
-
-#--- lds
-SECTIONS {
-  .text : { }
-  .sdata 0x07fc : { }
-  .data  0x17E0 : { }
-}
diff --git a/lld/test/ELF/riscv-relax-gp-partial-hi20-addend.s b/lld/test/ELF/riscv-relax-gp-partial-hi20-addend.s
deleted file mode 100644
index 9877ec8137e4ec..00000000000000
--- a/lld/test/ELF/riscv-relax-gp-partial-hi20-addend.s
+++ /dev/null
@@ -1,54 +0,0 @@
-# REQUIRES: riscv
-# RUN: rm -rf %t && split-file %s %t && cd %t
-
-# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o
-# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax 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 --no-show-raw-insn rv32 | FileCheck %s
-# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
- 
-# CHECK: 00001000 l       .data  {{0+}}08 Var0
-# CHECK: 00001f80 l       .data1 {{0+}}80 Var1
-# CHECK: 00001800 g       .sdata {{0+}}00 __global_pointer$
-
-# CHECK: <_start>:
-# CHECK-NEXT: lui     a1, 1
-# CHECK-NEXT: lw      a0, -2048(gp)
-# CHECK-NEXT: lw      a1, -2044(gp)
-# CHECK-NEXT: lui     a1, 2
-# CHECK-NEXT: lw      a0, 1920(gp)
-# CHECK-NEXT: lw      a1, 2044(gp)
-
-#--- a.s
-.global _start
-_start:
-        lui     a1, %hi(Var0+4)        # Cannot prove that %lo relocs will be reachable
-        lw      a0, %lo(Var0)(a1)      # Reachable from GP
-        lw      a1, %lo(Var0+4)(a1)    # Reachable from GP
-        lui     a1, %hi(Var1+124)      # Cannot prove that %lo relocs will be reachable
-        lw      a0, %lo(Var1)(a1)      # Reachable from GP
-        lw      a1, %lo(Var1+124)(a1)  # Reachable from GP
-
-.section .sdata,"aw"
-.section .data,"aw"
-  .p2align 3
-Var0:
-  .quad 0
-  .size   Var0, 8
-
-.section .data1,"aw"
-  .p2align 7
-Var1:
-  .quad 0
-  .zero 120
-  .size   Var1, 128
-
-#--- lds
-SECTIONS {
-  .text : { }
-  .sdata 0x1000 : { }
-  .data  0x1000 : { }
-  .data1 0x1f80 : { }
-}
diff --git a/lld/test/ELF/riscv-relax-gp-partial.s b/lld/test/ELF/riscv-relax-gp-partial.s
deleted file mode 100644
index fedc41f24e2d6a..00000000000000
--- a/lld/test/ELF/riscv-relax-gp-partial.s
+++ /dev/null
@@ -1,58 +0,0 @@
-# REQUIRES: riscv
-# RUN: rm -rf %t && split-file %s %t && cd %t
-
-# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o
-# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax 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 --no-show-raw-insn rv32 | FileCheck %s
-# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
- 
-# CHECK: 00000080 l       .data  {{0+}}08 Var0
-# CHECK: 00001000 l       .data  {{0+}}80 Var1
-# CHECK: 00000815 g       .sdata {{0+}}00 __global_pointer$
-
-# CHECK: <_start>:
-# CHECK-NOT:  lui
-# CHECK-NEXT: lw      a0, -1941(gp)
-# CHECK-NEXT: lw      a1, -1937(gp)
-# CHECK-NEXT: lui     a1, 1
-# CHECK-NEXT: lw      a0, 2027(gp)
-# CHECK-NEXT: lw      a1, 124(a1)
-
-#--- a.s
-.global _start
-_start:
-        lui     a1, %hi(Var0)
-        lw      a0, %lo(Var0)(a1)
-        lw      a1, %lo(Var0+4)(a1)
-        lui     a1, %hi(Var1)
-        lw      a0, %lo(Var1)(a1)      # First part is reachable from gp
-        lw      a1, %lo(Var1+124)(a1)  # The second part is not reachable
-
-.section .rodata
-foo:
-  .space 1
-.section .sdata,"aw"
-  .space 1
-.section .data,"aw"
-  .p2align 3
-Var0:
-  .quad 0
-  .size   Var0, 8
-  .space 3960
-  .p2align 7
-Var1:
-  .quad 0
-  .zero 120
-  .size   Var1, 128
-
-#--- lds
-SECTIONS {
-  .text : { }
-  .rodata : { }
-  .sdata : { }
-  .sbss : { }
-  .data : { }
-}

>From 075b2092e9248d9c9fb386cd97c0e99a71e833aa Mon Sep 17 00:00:00 2001
From: Nemanja Ivanovic <nemanja at synopsys.com>
Date: Thu, 4 Jan 2024 09:09:29 +0100
Subject: [PATCH 5/5] Simplify relaxation decision

Switch to using a somewhat loosely defined concept of accessible
blocks of memory to determine if all allowable relocations are
relaxable.
---
 lld/ELF/Arch/RISCV.cpp              | 52 +++++++++++++++++------------
 lld/test/ELF/riscv-relax-gp-edges.s | 48 +++++++++++++-------------
 2 files changed, 53 insertions(+), 47 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index ecbf8aaeeb3a51..4134a8281bff57 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -648,36 +648,44 @@ static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc,
   if (!gp)
     return;
 
-  if (!isInt<12>(r.sym->getVA(r.addend) - gp->getVA()))
+  int64_t theAddr = r.sym->getVA(r.addend);
+  uint64_t gpVA = gp->getVA();
+  if (!isInt<12>(theAddr - gpVA))
     return;
 
   // The symbol may be accessed in multiple pieces with different addends.
   // If we are relaxing the HI20 relocation, we need to ensure that we only
   // relax (and delete the instruction) if all possible LO12 relocations
-  // that depend on it will be relaxable. The compiler will only access multiple
-  // pieces of an object with low relocations on the memory op if the alignment
-  // allows it. Therefore it should suffice to check that the smaller of the
-  // alignment and size can be reached from GP.
-  uint32_t alignAdjust =
+  // that depend on it will be relaxable. Similarly, when relaxing the LO12
+  // relocation, we should ensure that the corresponding HI20 is also relaxed.
+  // Since relocations are not directly related to one another, we must rely on
+  // what the code generator is allowed to assume about their relationship.
+  // Namely, any LO12 relocation that relies on a HI20 relocation must not
+  // relocate an address in a different "accessible block" for an object.
+  // An "accessible block" is the block of memory with a size equal to the
+  // lower of the object's size and alignment.
+  // For example, it is legal to access an element of a naturaly aligned
+  // array of 8-byte objects with two different 4-byte loads.
+  // Element number 1 of an array of double can be accessed both as
+  // (%hi(arr+8),  %lo(arr+8), %lo(arr+12)) and
+  // (%hi(arr+12), %lo(arr+8), %lo(arr+12)).
+  // In any case, we only relax if the entire range [arr+8, arr+16) is reachable
+  // using GP-relative addressing.
+  uint32_t symAlign =
       r.sym->getOutputSection() ? r.sym->getOutputSection()->addralign : 0;
-  alignAdjust = std::min<uint32_t>(alignAdjust, r.sym->getSize());
-  if (alignAdjust)
-    alignAdjust--;
-
+  int64_t symSize = r.sym->getSize();
+  uint32_t reachWindow = std::min<uint32_t>(symAlign, symSize);
+  int64_t loAddr, hiAddr;
+  if (reachWindow != 0) {
+    loAddr = (theAddr / reachWindow) * reachWindow;
+    hiAddr = loAddr + reachWindow - 1;
+  } else
+    loAddr = hiAddr = theAddr;
+
+  if (!isInt<12>(loAddr - gpVA) || !isInt<12>(hiAddr - gpVA))
+    return;
   switch (r.type) {
   case R_RISCV_HI20: {
-    uint64_t hiAddr = r.sym->getVA(r.addend);
-    // If the addend is zero, the LO12 relocations can only be accessing the
-    // range [base, base+alignAdjust] (where base == r.sym->getVA()).
-    if (r.addend == 0 && !isInt<12>(hiAddr + alignAdjust - gp->getVA()))
-      return;
-
-    // However, if the addend is non-zero, the LO12 relocations may be accessing
-    // the range [HI-alignAdjust-1, HI+alignAdjust].
-    if (r.addend != 0 && (!isInt<12>(hiAddr - alignAdjust - 1 - gp->getVA()) ||
-                          !isInt<12>(hiAddr + alignAdjust - gp->getVA())))
-      return;
-
     // Remove lui rd, %hi20(x).
     sec.relaxAux->relocTypes[i] = R_RISCV_RELAX;
     remove = 4;
diff --git a/lld/test/ELF/riscv-relax-gp-edges.s b/lld/test/ELF/riscv-relax-gp-edges.s
index e355e242732907..b42d439a982bb4 100644
--- a/lld/test/ELF/riscv-relax-gp-edges.s
+++ b/lld/test/ELF/riscv-relax-gp-edges.s
@@ -8,25 +8,25 @@
 # RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax c.s -o rv32c.o
 # RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax c.s -o rv64c.o
 
-# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32a.o lds.a -o rv32a
-# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64a.o lds.a -o rv64a
-# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32b.o lds.b -o rv32b
-# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64b.o lds.b -o rv64b
-# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32c.o lds.c -o rv32c
-# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64c.o lds.c -o rv64c
-# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32a | FileCheck %s --check-prefix=CHECK-ALIGN
-# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64a | FileCheck %s --check-prefix=CHECK-ALIGN
-# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32b | FileCheck %s --check-prefix=CHECK-HI-ADD
-# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64b | FileCheck %s --check-prefix=CHECK-HI-ADD
-# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32c | FileCheck %s --check-prefix=CHECK-SIZE
-# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64c | FileCheck %s --check-prefix=CHECK-SIZE
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32a.o a.lds -o rv32a
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64a.o a.lds -o rv64a
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32b.o b.lds -o rv32b
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64b.o b.lds -o rv64b
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32c.o c.lds -o rv32c
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64c.o c.lds -o rv64c
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn --no-print-imm-hex rv32a | FileCheck %s --check-prefix=CHECK-ALIGN
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn --no-print-imm-hex rv64a | FileCheck %s --check-prefix=CHECK-ALIGN
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn --no-print-imm-hex rv32b | FileCheck %s --check-prefix=CHECK-HI-ADD
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn --no-print-imm-hex rv64b | FileCheck %s --check-prefix=CHECK-HI-ADD
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn --no-print-imm-hex rv32c | FileCheck %s --check-prefix=CHECK-SIZE
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn --no-print-imm-hex rv64c | FileCheck %s --check-prefix=CHECK-SIZE
  
 # CHECK-ALIGN: 000017e0 l       .data  {{0+}}80 Var1
 # CHECK-ALIGN: 00000ffc g       .sdata {{0+}}00 __global_pointer$
 
 # CHECK-ALIGN: <_start>:
 # CHECK-ALIGN-NEXT: lui     a1, 1
-# CHECK-ALIGN-NEXT: lw      a0, 2020(gp)
+# CHECK-ALIGN-NEXT: lw      a0, 2016(a1)
 # CHECK-ALIGN-NEXT: lw      a1, 2044(a1)
 
 #--- a.s
@@ -46,7 +46,7 @@ Var1:
   .zero 120
   .size   Var1, 128
 
-#--- lds.a
+#--- a.lds
 SECTIONS {
   .text : { }
   .sdata 0x07fc : { }
@@ -58,10 +58,8 @@ SECTIONS {
 # CHECK-HI-ADD: 00001800 g       .sdata {{0+}}00 __global_pointer$
 
 # CHECK-HI-ADD: <_start>:
-# CHECK-HI-ADD-NEXT: lui     a1, 1
 # CHECK-HI-ADD-NEXT: lw      a0, -2048(gp)
 # CHECK-HI-ADD-NEXT: lw      a1, -2044(gp)
-# CHECK-HI-ADD-NEXT: lui     a1, 2
 # CHECK-HI-ADD-NEXT: lw      a0, 1920(gp)
 # CHECK-HI-ADD-NEXT: lw      a1, 2044(gp)
 
@@ -71,12 +69,12 @@ SECTIONS {
 # the amount reachable from GP.
 .global _start
 _start:
-        lui     a1, %hi(Var0+4)        # Cannot prove that %lo relocs will be reachable
-        lw      a0, %lo(Var0)(a1)      # Reachable from GP
-        lw      a1, %lo(Var0+4)(a1)    # Reachable from GP
-        lui     a1, %hi(Var1+124)      # Cannot prove that %lo relocs will be reachable
-        lw      a0, %lo(Var1)(a1)      # Reachable from GP
-        lw      a1, %lo(Var1+124)(a1)  # Reachable from GP
+        lui     a1, %hi(Var0+4)
+        lw      a0, %lo(Var0)(a1)
+        lw      a1, %lo(Var0+4)(a1)
+        lui     a1, %hi(Var1+124)
+        lw      a0, %lo(Var1)(a1)
+        lw      a1, %lo(Var1+124)(a1)
 
 .section .sdata,"aw"
 .section .data,"aw"
@@ -92,7 +90,7 @@ Var1:
   .zero 120
   .size   Var1, 128
 
-#--- lds.b
+#--- b.lds
 SECTIONS {
   .text : { }
   .sdata 0x1000 : { }
@@ -109,7 +107,7 @@ SECTIONS {
 # CHECK-SIZE-NEXT: lw      a0, -1941(gp)
 # CHECK-SIZE-NEXT: lw      a1, -1937(gp)
 # CHECK-SIZE-NEXT: lui     a1, 1
-# CHECK-SIZE-NEXT: lw      a0, 2027(gp)
+# CHECK-SIZE-NEXT: lw      a0, 0(a1)
 # CHECK-SIZE-NEXT: lw      a1, 124(a1)
 
 #--- c.s
@@ -138,7 +136,7 @@ Var1:
   .zero 120
   .size   Var1, 128
 
-#--- lds.c
+#--- c.lds
 SECTIONS {
   .text : { }
   .sdata  0x0015 : { }



More information about the llvm-commits mailing list