[llvm] 3eb9b7d - [RISCV] Implement Relaxation for Xqcilb Jumps (#142702)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 10 07:33:24 PDT 2025


Author: Sam Elliott
Date: 2025-06-10T07:33:21-07:00
New Revision: 3eb9b7dbbd785673b88420dac87264cf79e432a3

URL: https://github.com/llvm/llvm-project/commit/3eb9b7dbbd785673b88420dac87264cf79e432a3
DIFF: https://github.com/llvm/llvm-project/commit/3eb9b7dbbd785673b88420dac87264cf79e432a3.diff

LOG: [RISCV] Implement Relaxation for Xqcilb Jumps (#142702)

Added: 
    llvm/test/MC/RISCV/rv32-relaxation-xqci.s

Modified: 
    llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
index e8570e4d32fe2..338e5a4772830 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
@@ -131,22 +131,42 @@ bool RISCVAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup,
     // For conditional branch instructions the immediate must be
     // in the range [-4096, 4094].
     return Offset > 4094 || Offset < -4096;
+  case RISCV::fixup_riscv_jal:
+    // For jump instructions the immediate must be in the range
+    // [-1048576, 1048574]
+    return Offset > 1048574 || Offset < -1048576;
   }
 }
 
 // Given a compressed control flow instruction this function returns
-// the expanded instruction.
-static unsigned getRelaxedOpcode(unsigned Op) {
-  switch (Op) {
-  default:
-    return Op;
+// the expanded instruction, or the original instruction code if no
+// expansion is available.
+static unsigned getRelaxedOpcode(const MCInst &Inst,
+                                 const MCSubtargetInfo &STI) {
+  switch (Inst.getOpcode()) {
   case RISCV::C_BEQZ:
     return RISCV::BEQ;
   case RISCV::C_BNEZ:
     return RISCV::BNE;
   case RISCV::C_J:
   case RISCV::C_JAL: // fall through.
+    // This only relaxes one "step" - i.e. from C.J to JAL, not from C.J to
+    // QC.E.J, because we can always relax again if needed.
     return RISCV::JAL;
+  case RISCV::JAL: {
+    // We can only relax JAL if we have Xqcilb
+    if (!STI.hasFeature(RISCV::FeatureVendorXqcilb))
+      break;
+
+    // And only if it is using X0 or X1 for rd.
+    MCRegister Reg = Inst.getOperand(0).getReg();
+    if (Reg == RISCV::X0)
+      return RISCV::QC_E_J;
+    if (Reg == RISCV::X1)
+      return RISCV::QC_E_JAL;
+
+    break;
+  }
   case RISCV::BEQ:
     return RISCV::PseudoLongBEQ;
   case RISCV::BNE:
@@ -184,6 +204,9 @@ static unsigned getRelaxedOpcode(unsigned Op) {
   case RISCV::QC_E_BGEUI:
     return RISCV::PseudoLongQC_E_BGEUI;
   }
+
+  // Returning the original opcode means we cannot relax the instruction.
+  return Inst.getOpcode();
 }
 
 void RISCVAsmBackend::relaxInstruction(MCInst &Inst,
@@ -201,6 +224,20 @@ void RISCVAsmBackend::relaxInstruction(MCInst &Inst,
   case RISCV::C_JAL: {
     [[maybe_unused]] bool Success = RISCVRVC::uncompress(Res, Inst, STI);
     assert(Success && "Can't uncompress instruction");
+    assert(Res.getOpcode() == getRelaxedOpcode(Inst, STI) &&
+           "Branch Relaxation Error");
+    break;
+  }
+  case RISCV::JAL: {
+    // This has to be written manually because the QC.E.J -> JAL is
+    // compression-only, so that it is not used when printing disassembly.
+    assert(STI.hasFeature(RISCV::FeatureVendorXqcilb) &&
+           "JAL is only relaxable with Xqcilb");
+    assert((Inst.getOperand(0).getReg() == RISCV::X0 ||
+            Inst.getOperand(0).getReg() == RISCV::X1) &&
+           "JAL only relaxable with rd=x0 or rd=x1");
+    Res.setOpcode(getRelaxedOpcode(Inst, STI));
+    Res.addOperand(Inst.getOperand(1));
     break;
   }
   case RISCV::BEQ:
@@ -221,7 +258,7 @@ void RISCVAsmBackend::relaxInstruction(MCInst &Inst,
   case RISCV::QC_E_BGEI:
   case RISCV::QC_E_BLTUI:
   case RISCV::QC_E_BGEUI:
-    Res.setOpcode(getRelaxedOpcode(Inst.getOpcode()));
+    Res.setOpcode(getRelaxedOpcode(Inst, STI));
     Res.addOperand(Inst.getOperand(0));
     Res.addOperand(Inst.getOperand(1));
     Res.addOperand(Inst.getOperand(2));
@@ -370,7 +407,7 @@ bool RISCVAsmBackend::mayNeedRelaxation(const MCInst &Inst,
   if (STI.hasFeature(RISCV::FeatureExactAssembly))
     return false;
 
-  return getRelaxedOpcode(Inst.getOpcode()) != Inst.getOpcode();
+  return getRelaxedOpcode(Inst, STI) != Inst.getOpcode();
 }
 
 bool RISCVAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count,

diff  --git a/llvm/test/MC/RISCV/rv32-relaxation-xqci.s b/llvm/test/MC/RISCV/rv32-relaxation-xqci.s
new file mode 100644
index 0000000000000..03c9914bb37d2
--- /dev/null
+++ b/llvm/test/MC/RISCV/rv32-relaxation-xqci.s
@@ -0,0 +1,170 @@
+# RUN: split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-xqcilb %t/pass.s -o - \
+# RUN:   | llvm-objdump -dr -M no-aliases - --mattr=+experimental-xqcilb | FileCheck %s
+# RUN: not llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-xqcilb %t/fail.s \
+# RUN:   2>&1 | FileCheck %t/fail.s --check-prefix=ERROR
+
+## This testcase shows how `c.j`, `c.jal` and `jal` can be relaxed to `qc.e.j` and `qc.e.jal`
+## with Xqcilb, when the branches are out of range, but also that these can be compressed
+## when referencing close labels.
+
+## The only problem we have here is when `jal` references an out-of-range label, and `rd` is
+## not `x0` or `x1` - for which we have no equivalent sequence, so we just fail to relax, and
+## emit a fixup-value-out-of-range error.
+
+#--- pass.s
+
+EXT_JUMP_NEGATIVE:
+  c.nop
+.space 0x100000
+
+FAR_JUMP_NEGATIVE:
+  c.nop
+.space 0x1000
+
+NEAR_NEGATIVE:
+  c.nop
+
+start:
+  c.j NEAR
+# CHECK: c.j {{0x[0-9a-f]+}} <NEAR>
+  c.j NEAR_NEGATIVE
+# CHECK: c.j {{0x[0-9a-f]+}} <NEAR_NEGATIVE>
+  c.j FAR_JUMP
+# CHECK: jal zero, {{0x[0-9a-f]+}} <FAR_JUMP>
+  c.j FAR_JUMP_NEGATIVE
+# CHECK: jal zero, {{0x[0-9a-f]+}} <FAR_JUMP_NEGATIVE>
+  c.j EXT_JUMP
+# CHECK: qc.e.j {{0x[0-9a-f]+}} <EXT_JUMP>
+  c.j EXT_JUMP_NEGATIVE
+# CHECK: qc.e.j {{0x[0-9a-f]+}} <EXT_JUMP_NEGATIVE>
+  c.j undef
+# CHECK: qc.e.j {{0x[0-9a-f]+}} <start+{{0x[0-9a-f]+}}>
+# CHECK: R_RISCV_CUSTOM195 undef
+
+  c.jal NEAR
+# CHECK: c.jal {{0x[0-9a-f]+}} <NEAR>
+  c.jal NEAR_NEGATIVE
+# CHECK: c.jal {{0x[0-9a-f]+}} <NEAR_NEGATIVE>
+  c.jal FAR_JUMP
+# CHECK: jal ra, {{0x[0-9a-f]+}} <FAR_JUMP>
+  c.jal FAR_JUMP_NEGATIVE
+# CHECK: jal ra, {{0x[0-9a-f]+}} <FAR_JUMP_NEGATIVE>
+  c.jal EXT_JUMP
+# CHECK: qc.e.jal {{0x[0-9a-f]+}} <EXT_JUMP>
+  c.jal EXT_JUMP_NEGATIVE
+# CHECK: qc.e.jal {{0x[0-9a-f]+}} <EXT_JUMP_NEGATIVE>
+  c.jal undef
+# CHECK: qc.e.jal {{0x[0-9a-f]+}} <start+{{0x[0-9a-f]+}}>
+# CHECK: R_RISCV_CUSTOM195 undef
+
+  jal zero, NEAR
+# CHECK: c.j {{0x[0-9a-f]+}} <NEAR>
+  jal zero, NEAR_NEGATIVE
+# CHECK: c.j {{0x[0-9a-f]+}} <NEAR_NEGATIVE>
+  jal zero, FAR_JUMP
+# CHECK: jal zero, {{0x[0-9a-f]+}} <FAR_JUMP>
+  jal zero, FAR_JUMP_NEGATIVE
+# CHECK: jal zero, {{0x[0-9a-f]+}} <FAR_JUMP_NEGATIVE>
+  jal zero, EXT_JUMP
+# CHECK: qc.e.j {{0x[0-9a-f]+}} <EXT_JUMP>
+  jal zero, EXT_JUMP_NEGATIVE
+# CHECK: qc.e.j {{0x[0-9a-f]+}} <EXT_JUMP_NEGATIVE>
+  jal zero, undef
+# CHECK: qc.e.j {{0x[0-9a-f]+}} <start+{{0x[0-9a-f]+}}>
+# CHECK: R_RISCV_CUSTOM195 undef
+
+  jal ra, NEAR
+# CHECK: c.jal {{0x[0-9a-f]+}} <NEAR>
+  jal ra, NEAR_NEGATIVE
+# CHECK: c.jal {{0x[0-9a-f]+}} <NEAR_NEGATIVE>
+  jal ra, FAR_JUMP
+# CHECK: jal ra, {{0x[0-9a-f]+}} <FAR_JUMP>
+  jal ra, FAR_JUMP_NEGATIVE
+# CHECK: jal ra, {{0x[0-9a-f]+}} <FAR_JUMP_NEGATIVE>
+  jal ra, EXT_JUMP
+# CHECK: qc.e.jal {{0x[0-9a-f]+}} <EXT_JUMP>
+  jal ra, EXT_JUMP_NEGATIVE
+# CHECK: qc.e.jal {{0x[0-9a-f]+}} <EXT_JUMP_NEGATIVE>
+  jal ra, undef
+# CHECK: qc.e.jal {{0x[0-9a-f]+}} <start+{{0x[0-9a-f]+}}>
+# CHECK: R_RISCV_CUSTOM195 undef
+
+  qc.e.j NEAR
+# CHECK: c.j {{0x[0-9a-f]+}} <NEAR>
+  qc.e.j NEAR_NEGATIVE
+# CHECK: c.j {{0x[0-9a-f]+}} <NEAR_NEGATIVE>
+  qc.e.j FAR_JUMP
+# CHECK: jal zero, {{0x[0-9a-f]+}} <FAR_JUMP>
+  qc.e.j FAR_JUMP_NEGATIVE
+# CHECK: jal zero, {{0x[0-9a-f]+}} <FAR_JUMP_NEGATIVE>
+  qc.e.j EXT_JUMP
+# CHECK: qc.e.j {{0x[0-9a-f]+}} <EXT_JUMP>
+  qc.e.j EXT_JUMP_NEGATIVE
+# CHECK: qc.e.j {{0x[0-9a-f]+}} <EXT_JUMP_NEGATIVE>
+  qc.e.j undef
+# CHECK: qc.e.j {{0x[0-9a-f]+}} <start+{{0x[0-9a-f]+}}>
+# CHECK: R_RISCV_CUSTOM195 undef
+
+  qc.e.jal NEAR
+# CHECK: c.jal {{0x[0-9a-f]+}} <NEAR>
+  qc.e.jal NEAR_NEGATIVE
+# CHECK: c.jal {{0x[0-9a-f]+}} <NEAR_NEGATIVE>
+  qc.e.jal FAR_JUMP
+# CHECK: jal ra, {{0x[0-9a-f]+}} <FAR_JUMP>
+  qc.e.jal FAR_JUMP_NEGATIVE
+# CHECK: jal ra, {{0x[0-9a-f]+}} <FAR_JUMP_NEGATIVE>
+  qc.e.jal EXT_JUMP
+# CHECK: qc.e.jal {{0x[0-9a-f]+}} <EXT_JUMP>
+  qc.e.jal EXT_JUMP_NEGATIVE
+# CHECK: qc.e.jal {{0x[0-9a-f]+}} <EXT_JUMP_NEGATIVE>
+  qc.e.jal undef
+# CHECK: qc.e.jal {{0x[0-9a-f]+}} <start+{{0x[0-9a-f]+}}>
+# CHECK: R_RISCV_CUSTOM195 undef
+
+
+
+  jal t1, NEAR
+# CHECK: jal t1, {{0x[0-9a-f]+}} <NEAR>
+  jal t1, NEAR_NEGATIVE
+# CHECK: jal t1, {{0x[0-9a-f]+}} <NEAR_NEGATIVE>
+  jal t1, FAR_JUMP
+# CHECK: jal t1, {{0x[0-9a-f]+}} <FAR_JUMP>
+  jal t1, FAR_JUMP_NEGATIVE
+# CHECK: jal t1, {{0x[0-9a-f]+}} <FAR_JUMP_NEGATIVE>
+
+## The two cases with EXT_JUMP and EXT_JUMP_NEGATIVE are
+## in fail.s, below.
+
+  jal t1, undef
+# CHECK: jal t1, {{0x[0-9a-f]+}} <start+{{0x[0-9a-f]+}}>
+# CHECK: R_RISCV_JAL undef
+
+
+NEAR:
+  c.nop
+.space 0x1000
+FAR_JUMP:
+  c.nop
+.space 0x100000
+EXT_JUMP:
+  c.nop
+
+
+#--- fail.s
+
+
+EXT_JUMP_NEGATIVE:
+  c.nop
+.space 0x100000
+.space 0x1000
+
+  jal t1, EXT_JUMP
+# ERROR: [[@LINE-1]]:3: error: fixup value out of range
+  jal t1, EXT_JUMP_NEGATIVE
+# ERROR: [[@LINE-1]]:3: error: fixup value out of range
+
+.space 0x1000
+.space 0x100000
+EXT_JUMP:
+  c.nop


        


More information about the llvm-commits mailing list