[llvm] 34d9eab - [RISCV] Support getJumpTableIndex (#224197)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 23 18:21:16 PDT 2026
Author: Piyou Chen
Date: 2026-09-24T09:21:10+08:00
New Revision: 34d9eab9d702da1c63fd82349ec9cb10834f3ade
URL: https://github.com/llvm/llvm-project/commit/34d9eab9d702da1c63fd82349ec9cb10834f3ade
DIFF: https://github.com/llvm/llvm-project/commit/34d9eab9d702da1c63fd82349ec9cb10834f3ade.diff
LOG: [RISCV] Support getJumpTableIndex (#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, to sink it out of the jump table dispatch block,
MachineSink needs to split the critical edge, which requires updating
the jump table entry. It needs the getJumpTableIndex hook to know which
jump table to update.
This avoids unnecessary instructions in the hot path (the jump table
dispatch block).
Added:
llvm/test/CodeGen/RISCV/machine-sink-jumptable-edge-split.ll
Modified:
llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
llvm/lib/Target/RISCV/RISCVInstrInfo.h
Removed:
################################################################################
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index b057c6066516f..161fc86b9aa6a 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -1817,6 +1817,81 @@ 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::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 491800e620de4..859e9ec7fbc9f 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h
@@ -181,6 +181,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
new file mode 100644
index 0000000000000..b98440eb49255
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/machine-sink-jumptable-edge-split.ll
@@ -0,0 +1,169 @@
+; 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_14
+; CHECK-NEXT: # %bb.4: # %disp
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: sh2add a0, a0, s2
+; 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
+; 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: # %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_14: # %c0
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 100
+; CHECK-NEXT: j .LBB0_2
+; 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_16: # %c9
+; CHECK-NEXT: # in Loop: Header=BB0_3 Depth=1
+; CHECK-NEXT: li a0, 109
+; CHECK-NEXT: j .LBB0_2
+; CHECK-NEXT: .LBB0_17: # %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
+}
More information about the llvm-commits
mailing list