[llvm] [RISCV] Support getJumpTableIndex (PR #224197)
Piyou Chen via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 16 22:59:08 PDT 2026
https://github.com/BeMg created https://github.com/llvm/llvm-project/pull/224197
This patch implements getJumpTableIndex hook for RISC-V; it trace from
PseudoBRIND back to the %jump-table.N.
The li instruction in jumptable dispatch block exists because the phi
constant is emitted during instruction selection. LLVM chooses the
source block in which to place the phi constant, then postpones moving
it to MachineSink, which relocates the phi constant closer to its use
site. However, MachineSink can't understand the jump table, so it needs
a getJumpTableIndex hook to tell it how to resolve this.
This avoids unnecessary instructions in the hot path (the jump table dispatch block).
>From fb8957fe39895cea5aafe1dff5d7e9b4d84d401e Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Wed, 16 Sep 2026 22:52:12 -0700
Subject: [PATCH 1/2] [NFC] precommit test
---
.../machine-sink-jumptable-edge-split.ll | 167 ++++++++++++++++++
1 file changed, 167 insertions(+)
create mode 100644 llvm/test/CodeGen/RISCV/machine-sink-jumptable-edge-split.ll
diff --git a/llvm/test/CodeGen/RISCV/machine-sink-jumptable-edge-split.ll b/llvm/test/CodeGen/RISCV/machine-sink-jumptable-edge-split.ll
new file mode 100644
index 00000000000000..86a77c2866956c
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/machine-sink-jumptable-edge-split.ll
@@ -0,0 +1,167 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4
+; RUN: llc -mtriple=riscv64 -mattr=+zba -O3 < %s | FileCheck %s
+
+declare void @use(i32)
+
+define void @sink_through_jt(ptr %p) {
+; CHECK-LABEL: sink_through_jt:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: addi sp, sp, -32
+; CHECK-NEXT: .cfi_def_cfa_offset 32
+; CHECK-NEXT: sd ra, 24(sp) # 8-byte Folded Spill
+; CHECK-NEXT: sd s0, 16(sp) # 8-byte Folded Spill
+; CHECK-NEXT: sd s1, 8(sp) # 8-byte Folded Spill
+; CHECK-NEXT: sd s2, 0(sp) # 8-byte Folded Spill
+; CHECK-NEXT: .cfi_offset ra, -8
+; CHECK-NEXT: .cfi_offset s0, -16
+; CHECK-NEXT: .cfi_offset s1, -24
+; CHECK-NEXT: .cfi_offset s2, -32
+; CHECK-NEXT: li s0, 12
+; CHECK-NEXT: addi s1, a0, 1
+; CHECK-NEXT: lui s2, %hi(.LJTI0_0)
+; CHECK-NEXT: addi s2, s2, %lo(.LJTI0_0)
+; CHECK-NEXT: j .LBB0_3
+; CHECK-NEXT: .LBB0_1: # %c10
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 110
+; CHECK-NEXT: .LBB0_2: # %m
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: call use
+; CHECK-NEXT: addi s1, s1, 1
+; CHECK-NEXT: .LBB0_3: # %disp
+; CHECK-NEXT: # =>This Inner Loop Header: Depth=1
+; CHECK-NEXT: lbu a0, -1(s1)
+; CHECK-NEXT: addi a0, a0, -1
+; CHECK-NEXT: bltu s0, a0, .LBB0_13
+; CHECK-NEXT: # %bb.4: # %disp
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: sh2add a0, a0, s2
+; CHECK-NEXT: lw a1, 0(s1)
+; CHECK-NEXT: lw a2, 0(a0)
+; CHECK-NEXT: addiw a0, a1, 7
+; CHECK-NEXT: jr a2
+; CHECK-NEXT: .LBB0_5: # %other
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 55
+; CHECK-NEXT: call use
+; CHECK-NEXT: li a0, 0
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_6: # %c3
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 103
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_7: # %c11
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 111
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_8: # %c8
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 108
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_9: # %c1
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 101
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_10: # %c2
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 102
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_11: # %c6
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 106
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_12: # %c4
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 104
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_13: # %c0
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 100
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_14: # %c5
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 105
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_15: # %c9
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 109
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_16: # %c7
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 107
+; CHECK-NEXT: j .LBB0_2
+entry:
+ br label %disp
+
+disp:
+ %ip = phi ptr [ %p, %entry ], [ %ipn, %c0 ], [ %ipn, %c1 ], [ %ipn, %c2 ],
+ [ %ipn, %c3 ], [ %ipn, %c4 ], [ %ipn, %c5 ], [ %ipn, %c6 ],
+ [ %ipn, %c7 ], [ %ipn, %c8 ], [ %ipn, %c9 ], [ %ipn, %c10 ],
+ [ %ipn, %c11 ], [ %ipn, %m ]
+ %op = load i8, ptr %ip
+ %ipn = getelementptr i8, ptr %ip, i64 1
+ %x = load i32, ptr %ipn
+ %v = add i32 %x, 7
+ switch i8 %op, label %c0 [
+ i8 1, label %m
+ i8 2, label %other
+ i8 3, label %c1
+ i8 4, label %c2
+ i8 5, label %c3
+ i8 6, label %c4
+ i8 7, label %c5
+ i8 8, label %c6
+ i8 9, label %c7
+ i8 10, label %c8
+ i8 11, label %c9
+ i8 12, label %c10
+ i8 13, label %c11
+ ]
+
+; Second predecessor of %m, so that %disp -> %m is a critical edge.
+other:
+ call void @use(i32 55)
+ br label %m
+
+m:
+ %phi = phi i32 [ %v, %disp ], [ 0, %other ]
+ call void @use(i32 %phi)
+ br label %disp
+
+c0:
+ call void @use(i32 100)
+ br label %disp
+c1:
+ call void @use(i32 101)
+ br label %disp
+c2:
+ call void @use(i32 102)
+ br label %disp
+c3:
+ call void @use(i32 103)
+ br label %disp
+c4:
+ call void @use(i32 104)
+ br label %disp
+c5:
+ call void @use(i32 105)
+ br label %disp
+c6:
+ call void @use(i32 106)
+ br label %disp
+c7:
+ call void @use(i32 107)
+ br label %disp
+c8:
+ call void @use(i32 108)
+ br label %disp
+c9:
+ call void @use(i32 109)
+ br label %disp
+c10:
+ call void @use(i32 110)
+ br label %disp
+c11:
+ call void @use(i32 111)
+ br label %disp
+}
>From 504537d0fb313fa5c8e6c681e1d3118a11a79af4 Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Wed, 16 Sep 2026 22:55:06 -0700
Subject: [PATCH 2/2] [RISCV] Support getJumpTableIndex
---
llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 76 +++++++++++++++++++
llvm/lib/Target/RISCV/RISCVInstrInfo.h | 2 +
.../machine-sink-jumptable-edge-split.ll | 32 ++++----
3 files changed, 95 insertions(+), 15 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index 6d83480fa6c691..91559ab0e442ec 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -1817,6 +1817,82 @@ bool RISCVInstrInfo::isBranchOffsetInRange(unsigned BranchOp,
}
}
+static bool isJumpTableLoad(const MachineInstr &MI) {
+ return any_of(MI.memoperands(), [](const MachineMemOperand *MMO) {
+ const PseudoSourceValue *PSV = MMO->getPseudoValue();
+ return PSV && PSV->isJumpTable();
+ });
+}
+
+/// Walk back from \p Reg through a jump-table address computation and return
+/// the index of the table it reads, or -1 if \p Reg is not part of one.
+static int getJumpTableIndexFromReg(const MachineRegisterInfo &MRI,
+ Register Reg, unsigned Depth) {
+ // Set the limit for the recursive search depth.
+ constexpr unsigned MaxDepth = 6;
+ if (Depth > MaxDepth)
+ return -1;
+
+ if (!Reg.isVirtual())
+ return -1;
+
+ const MachineInstr *MI = MRI.getUniqueVRegDef(Reg);
+ if (!MI)
+ return -1;
+
+ // MI has jump table operand, return it.
+ for (const MachineOperand &MO : MI->operands())
+ if (MO.isJTI())
+ return MO.getIndex();
+
+ // Handle intermediate instructions when resolving to the JumpTableIndex.
+ switch (MI->getOpcode()) {
+ case RISCV::ADD:
+ case RISCV::ADDI:
+ case RISCV::SLLI:
+ case RISCV::SH1ADD:
+ case RISCV::SH2ADD:
+ case RISCV::SH3ADD:
+ break;
+ case RISCV::LW:
+ case RISCV::LWU:
+ case RISCV::LD:
+ // Only consider ::(load from jump-table) kind of load.
+ if (!isJumpTableLoad(*MI))
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+
+ for (const MachineOperand &MO : MI->all_uses())
+ if (int JTI = getJumpTableIndexFromReg(MRI, MO.getReg(), Depth + 1);
+ JTI >= 0)
+ return JTI;
+
+ return -1;
+}
+
+// Recursively search for %jump-table.N starting from PseudoBRIND,
+// and return the index of %jump-table.N.
+//
+// One common jump table:
+//
+// %base = PseudoMovAddr/PseudoLLA/LUI(+ADDI)/QC_E_LI %jump-table.N
+// %addr = SH2ADD %index, %base
+// %entry = LW %addr, 0 :: (load from jump-table)
+// %target = ADD %entry, %base
+// PseudoBRIND %target, 0
+//
+int RISCVInstrInfo::getJumpTableIndex(const MachineInstr &MI) const {
+ if (MI.getOpcode() != RISCV::PseudoBRIND &&
+ MI.getOpcode() != RISCV::PseudoBRINDX7)
+ return -1;
+
+ const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
+ return getJumpTableIndexFromReg(MRI, MI.getOperand(0).getReg(), 0);
+}
+
// If the operation has a predicated pseudo instruction, return the pseudo
// instruction opcode. Otherwise, return RISCV::INSTRUCTION_LIST_END.
// TODO: Support more operations.
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.h b/llvm/lib/Target/RISCV/RISCVInstrInfo.h
index 3f860c93e10b90..ac196ff5700800 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h
@@ -176,6 +176,8 @@ class RISCVInstrInfo : public RISCVGenInstrInfo {
bool isBranchOffsetInRange(unsigned BranchOpc,
int64_t BrOffset) const override;
+ int getJumpTableIndex(const MachineInstr &MI) const override;
+
MachineInstr *optimizeSelect(MachineInstr &MI,
SmallPtrSetImpl<MachineInstr *> &SeenMIs,
bool) const override;
diff --git a/llvm/test/CodeGen/RISCV/machine-sink-jumptable-edge-split.ll b/llvm/test/CodeGen/RISCV/machine-sink-jumptable-edge-split.ll
index 86a77c2866956c..b98440eb492553 100644
--- a/llvm/test/CodeGen/RISCV/machine-sink-jumptable-edge-split.ll
+++ b/llvm/test/CodeGen/RISCV/machine-sink-jumptable-edge-split.ll
@@ -32,19 +32,15 @@ define void @sink_through_jt(ptr %p) {
; CHECK-NEXT: # =>This Inner Loop Header: Depth=1
; CHECK-NEXT: lbu a0, -1(s1)
; CHECK-NEXT: addi a0, a0, -1
-; CHECK-NEXT: bltu s0, a0, .LBB0_13
+; CHECK-NEXT: bltu s0, a0, .LBB0_14
; CHECK-NEXT: # %bb.4: # %disp
; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
; CHECK-NEXT: sh2add a0, a0, s2
-; CHECK-NEXT: lw a1, 0(s1)
-; CHECK-NEXT: lw a2, 0(a0)
-; CHECK-NEXT: addiw a0, a1, 7
-; CHECK-NEXT: jr a2
-; CHECK-NEXT: .LBB0_5: # %other
-; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
-; CHECK-NEXT: li a0, 55
-; CHECK-NEXT: call use
-; CHECK-NEXT: li a0, 0
+; CHECK-NEXT: lw a0, 0(a0)
+; CHECK-NEXT: jr a0
+; CHECK-NEXT: .LBB0_5: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: lw a0, 0(s1)
+; CHECK-NEXT: addiw a0, a0, 7
; CHECK-NEXT: j .LBB0_2
; CHECK-NEXT: .LBB0_6: # %c3
; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
@@ -70,23 +66,29 @@ define void @sink_through_jt(ptr %p) {
; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
; CHECK-NEXT: li a0, 106
; CHECK-NEXT: j .LBB0_2
-; CHECK-NEXT: .LBB0_12: # %c4
+; CHECK-NEXT: .LBB0_12: # %other
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 55
+; CHECK-NEXT: call use
+; CHECK-NEXT: li a0, 0
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_13: # %c4
; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
; CHECK-NEXT: li a0, 104
; CHECK-NEXT: j .LBB0_2
-; CHECK-NEXT: .LBB0_13: # %c0
+; CHECK-NEXT: .LBB0_14: # %c0
; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
; CHECK-NEXT: li a0, 100
; CHECK-NEXT: j .LBB0_2
-; CHECK-NEXT: .LBB0_14: # %c5
+; CHECK-NEXT: .LBB0_15: # %c5
; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
; CHECK-NEXT: li a0, 105
; CHECK-NEXT: j .LBB0_2
-; CHECK-NEXT: .LBB0_15: # %c9
+; CHECK-NEXT: .LBB0_16: # %c9
; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
; CHECK-NEXT: li a0, 109
; CHECK-NEXT: j .LBB0_2
-; CHECK-NEXT: .LBB0_16: # %c7
+; CHECK-NEXT: .LBB0_17: # %c7
; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
; CHECK-NEXT: li a0, 107
; CHECK-NEXT: j .LBB0_2
More information about the llvm-commits
mailing list