-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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
base: main
Are you sure you want to change the base?
Conversation
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.
@llvm/pr-subscribers-lld @llvm/pr-subscribers-lld-elf Author: Philip Reames (preames) ChangesIf 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:
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: |
There was a problem hiding this comment.
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.
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.