[llvm] [RISCV][GlobalISel] Legalize and select G_PREFETCH (PR #215466)
Kane Wang via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 12 02:44:30 PDT 2026
https://github.com/ReVe1uv updated https://github.com/llvm/llvm-project/pull/215466
>From d39c71f759491dc330edffc8d4953d363e1e7206 Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Tue, 11 Aug 2026 14:16:35 +0800
Subject: [PATCH] [RISCV][GlobalISel] Legalize and select G_PREFETCH
GlobalISel aborted on llvm.prefetch ("unable to legalize G_PREFETCH")
while SDAG lowers it to prefetch.r/w/i. Add a legalizer rule marking
G_PREFETCH legal for p0 addresses, and a custom selector since the
SDAG patterns use the AddrRegImmLsb00000 complex pattern that the GISel
pattern importer does not auto-translate. The selector picks
prefetch.r/w/i from the rw/cache-type immediates and folds a
simm12_lsb00000 offset when present, matching SDAG.
---
.../RISCV/GISel/RISCVInstructionSelector.cpp | 77 ++++++++++++
.../Target/RISCV/GISel/RISCVLegalizerInfo.cpp | 2 +
.../GlobalISel/legalizer-info-validation.mir | 4 +-
.../test/CodeGen/RISCV/GlobalISel/prefetch.ll | 116 ++++++++++++++++++
4 files changed, 197 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/prefetch.ll
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
index 16af89c715861..5e8e7fd4c18f5 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
@@ -98,6 +98,7 @@ class RISCVInstructionSelector : public InstructionSelector {
bool selectIntrinsic(MachineInstr &I) const;
bool selectExtractSubvector(MachineInstr &MI) const;
bool selectInsertSubVector(MachineInstr &I) const;
+ bool selectPrefetch(MachineInstr &MI) const;
ComplexRendererFns selectShiftMask(MachineOperand &Root,
unsigned ShiftWidth) const;
ComplexRendererFns selectShiftMaskXLen(MachineOperand &Root) const {
@@ -107,6 +108,7 @@ class RISCVInstructionSelector : public InstructionSelector {
return selectShiftMask(Root, 32);
}
ComplexRendererFns selectAddrRegImm(MachineOperand &Root) const;
+ ComplexRendererFns selectAddrRegImmLsb00000(MachineOperand &Root) const;
ComplexRendererFns selectSExtBits(MachineOperand &Root, unsigned Bits) const;
template <unsigned Bits>
@@ -593,6 +595,42 @@ RISCVInstructionSelector::selectAddrRegImm(MachineOperand &Root) const {
[=](MachineInstrBuilder &MIB) { MIB.addImm(0); }}};
}
+InstructionSelector::ComplexRendererFns
+RISCVInstructionSelector::selectAddrRegImmLsb00000(MachineOperand &Root) const {
+ if (!Root.isReg())
+ return std::nullopt;
+
+ MachineInstr *RootDef = MRI->getVRegDef(Root.getReg());
+ if (RootDef->getOpcode() == TargetOpcode::G_FRAME_INDEX) {
+ return {{
+ [=](MachineInstrBuilder &MIB) { MIB.add(RootDef->getOperand(1)); },
+ [=](MachineInstrBuilder &MIB) { MIB.addImm(0); },
+ }};
+ }
+
+ if (isBaseWithConstantOffset(Root, *MRI)) {
+ MachineOperand &LHS = RootDef->getOperand(1);
+ MachineOperand &RHS = RootDef->getOperand(2);
+ MachineInstr *LHSDef = MRI->getVRegDef(LHS.getReg());
+ MachineInstr *RHSDef = MRI->getVRegDef(RHS.getReg());
+ int64_t RHSC = RHSDef->getOperand(1).getCImm()->getSExtValue();
+ if (isInt<12>(RHSC) && (RHSC % 32 == 0)) {
+ if (LHSDef->getOpcode() == TargetOpcode::G_FRAME_INDEX)
+ return {{
+ [=](MachineInstrBuilder &MIB) { MIB.add(LHSDef->getOperand(1)); },
+ [=](MachineInstrBuilder &MIB) { MIB.addImm(RHSC); },
+ }};
+ return {{[=](MachineInstrBuilder &MIB) { MIB.add(LHS); },
+ [=](MachineInstrBuilder &MIB) { MIB.addImm(RHSC); }}};
+ }
+ }
+
+ return {{[=](MachineInstrBuilder &MIB) { MIB.addReg(Root.getReg()); },
+ [=](MachineInstrBuilder &MIB) { MIB.addImm(0); }}};
+// TODO: fold large-constant offsets (ADDI adjustment / Hi-Lo12 split) like
+// SDAG's SelectAddrRegImmLsb00000; load/store share this gap.
+}
+
/// Returns the RISCVCC::CondCode that corresponds to the CmpInst::Predicate CC.
/// CC Must be an ICMP Predicate.
static RISCVCC::CondCode getRISCVCCFromICmp(CmpInst::Predicate CC) {
@@ -1444,11 +1482,50 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
return selectExtractSubvector(MI);
case TargetOpcode::G_INSERT_SUBVECTOR:
return selectInsertSubVector(MI);
+ case TargetOpcode::G_PREFETCH:
+ return selectPrefetch(MI);
default:
return false;
}
}
+bool RISCVInstructionSelector::selectPrefetch(MachineInstr &MI) const {
+ assert(MI.getOpcode() == TargetOpcode::G_PREFETCH);
+ // G_PREFETCH operands: 0=address, 1=rw, 2=locality, 3=cache type.
+ int64_t IsWrite = MI.getOperand(1).getImm();
+ int64_t IsData = MI.getOperand(3).getImm();
+ unsigned Opc;
+ if (!IsData)
+ Opc = RISCV::PREFETCH_I;
+ else if (!IsWrite)
+ Opc = RISCV::PREFETCH_R;
+ else
+ Opc = RISCV::PREFETCH_W;
+
+ // Can only handle AddressSpace 0.
+ LLT PtrTy = MRI->getType(MI.getOperand(0).getReg());
+ if (PtrTy.getAddressSpace() != 0)
+ return false;
+
+ // Fold a simm12_lsb00000 offset into the prefetch immediate when possible;
+ // otherwise use the address as-is with a zero offset. Mirrors how load/store
+ // use selectAddrRegImm.
+ auto AddrModeFns = selectAddrRegImmLsb00000(MI.getOperand(0));
+ if (!AddrModeFns)
+ return false;
+
+ MachineInstrBuilder NewInst =
+ BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII.get(Opc));
+ NewInst.setMIFlags(MI.getFlags());
+ NewInst.cloneMemRefs(MI);
+ for (auto &Fn : *AddrModeFns)
+ Fn(NewInst);
+
+ MI.eraseFromParent();
+ constrainSelectedInstRegOperands(*NewInst, TII, TRI, RBI);
+ return true;
+}
+
bool RISCVInstructionSelector::selectUnmergeValues(MachineInstr &MI) const {
assert(MI.getOpcode() == TargetOpcode::G_UNMERGE_VALUES);
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index c5459d862b03f..78d96d69344e6 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -777,6 +777,8 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
.clampScalar(0, sXLen, sXLen)
.unsupported();
+ getActionDefinitionsBuilder(G_PREFETCH).legalFor({p0});
+
LegalityPredicate InsertVectorEltPred = [=](const LegalityQuery &Query) {
LLT VecTy = Query.Types[0];
LLT EltTy = Query.Types[1];
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
index 1770bc6430eb9..280ec7cd1e0f5 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
@@ -321,8 +321,8 @@
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
# DEBUG-NEXT: G_PREFETCH (opcode {{[0-9]+}}): 1 type index, 0 imm indices
-# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
-# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
+# DEBUG-NEXT: .. the first uncovered type index: 1, OK
+# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
# DEBUG-NEXT: G_BRCOND (opcode {{[0-9]+}}): 1 type index, 0 imm indices
# DEBUG-NEXT: .. the first uncovered type index: 1, OK
# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/prefetch.ll b/llvm/test/CodeGen/RISCV/GlobalISel/prefetch.ll
new file mode 100644
index 0000000000000..c725b37c0812f
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/prefetch.ll
@@ -0,0 +1,116 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+zicbop -global-isel -global-isel-abort=1 \
+; RUN: < %s | FileCheck %s --check-prefix=RV32ZICBOP
+; RUN: llc -mtriple=riscv64 -mattr=+zicbop -global-isel -global-isel-abort=1 \
+; RUN: < %s | FileCheck %s --check-prefix=RV64ZICBOP
+; RUN: llc -mtriple=riscv64 -global-isel -global-isel-abort=1 \
+; RUN: < %s | FileCheck %s --check-prefix=RV64
+
+; prefetch hints are available even without Zicbop per the psABI, so the
+; RV64 run (no +zicbop) still emits prefetch.r/w/i.
+
+define void @prefetch_r(ptr %a) nounwind {
+; RV32ZICBOP-LABEL: prefetch_r:
+; RV32ZICBOP: # %bb.0:
+; RV32ZICBOP-NEXT: prefetch.r 0(a0)
+; RV32ZICBOP-NEXT: ret
+;
+; RV64ZICBOP-LABEL: prefetch_r:
+; RV64ZICBOP: # %bb.0:
+; RV64ZICBOP-NEXT: prefetch.r 0(a0)
+; RV64ZICBOP-NEXT: ret
+;
+; RV64-LABEL: prefetch_r:
+; RV64: # %bb.0:
+; RV64-NEXT: prefetch.r 0(a0)
+; RV64-NEXT: ret
+ call void @llvm.prefetch(ptr %a, i32 0, i32 3, i32 1)
+ ret void
+}
+
+define void @prefetch_w(ptr %a) nounwind {
+; RV32ZICBOP-LABEL: prefetch_w:
+; RV32ZICBOP: # %bb.0:
+; RV32ZICBOP-NEXT: prefetch.w 0(a0)
+; RV32ZICBOP-NEXT: ret
+;
+; RV64ZICBOP-LABEL: prefetch_w:
+; RV64ZICBOP: # %bb.0:
+; RV64ZICBOP-NEXT: prefetch.w 0(a0)
+; RV64ZICBOP-NEXT: ret
+;
+; RV64-LABEL: prefetch_w:
+; RV64: # %bb.0:
+; RV64-NEXT: prefetch.w 0(a0)
+; RV64-NEXT: ret
+ call void @llvm.prefetch(ptr %a, i32 1, i32 3, i32 1)
+ ret void
+}
+
+define void @prefetch_i(ptr %a) nounwind {
+; RV32ZICBOP-LABEL: prefetch_i:
+; RV32ZICBOP: # %bb.0:
+; RV32ZICBOP-NEXT: prefetch.i 0(a0)
+; RV32ZICBOP-NEXT: ret
+;
+; RV64ZICBOP-LABEL: prefetch_i:
+; RV64ZICBOP: # %bb.0:
+; RV64ZICBOP-NEXT: prefetch.i 0(a0)
+; RV64ZICBOP-NEXT: ret
+;
+; RV64-LABEL: prefetch_i:
+; RV64: # %bb.0:
+; RV64-NEXT: prefetch.i 0(a0)
+; RV64-NEXT: ret
+ call void @llvm.prefetch(ptr %a, i32 0, i32 3, i32 0)
+ ret void
+}
+
+; A constant offset that is a multiple of 32 (simm12_lsb00000) folds into the
+; prefetch immediate.
+define void @prefetch_r_aligned_offset(ptr %a) nounwind {
+; RV32ZICBOP-LABEL: prefetch_r_aligned_offset:
+; RV32ZICBOP: # %bb.0:
+; RV32ZICBOP-NEXT: prefetch.r 64(a0)
+; RV32ZICBOP-NEXT: ret
+;
+; RV64ZICBOP-LABEL: prefetch_r_aligned_offset:
+; RV64ZICBOP: # %bb.0:
+; RV64ZICBOP-NEXT: prefetch.r 64(a0)
+; RV64ZICBOP-NEXT: ret
+;
+; RV64-LABEL: prefetch_r_aligned_offset:
+; RV64: # %bb.0:
+; RV64-NEXT: prefetch.r 64(a0)
+; RV64-NEXT: ret
+ %q = getelementptr i8, ptr %a, i32 64
+ call void @llvm.prefetch(ptr %q, i32 0, i32 3, i32 1)
+ ret void
+}
+
+; An offset that is not a multiple of 32 cannot be encoded in simm12_lsb00000;
+; the address is materialized with an ADDI and a zero offset is used.
+define void @prefetch_r_nonaligned_offset(ptr %a) nounwind {
+; RV32ZICBOP-LABEL: prefetch_r_nonaligned_offset:
+; RV32ZICBOP: # %bb.0:
+; RV32ZICBOP-NEXT: addi a0, a0, 4
+; RV32ZICBOP-NEXT: prefetch.r 0(a0)
+; RV32ZICBOP-NEXT: ret
+;
+; RV64ZICBOP-LABEL: prefetch_r_nonaligned_offset:
+; RV64ZICBOP: # %bb.0:
+; RV64ZICBOP-NEXT: addi a0, a0, 4
+; RV64ZICBOP-NEXT: prefetch.r 0(a0)
+; RV64ZICBOP-NEXT: ret
+;
+; RV64-LABEL: prefetch_r_nonaligned_offset:
+; RV64: # %bb.0:
+; RV64-NEXT: addi a0, a0, 4
+; RV64-NEXT: prefetch.r 0(a0)
+; RV64-NEXT: ret
+ %q = getelementptr i8, ptr %a, i32 4
+ call void @llvm.prefetch(ptr %q, i32 0, i32 3, i32 1)
+ ret void
+}
+
+declare void @llvm.prefetch(ptr, i32, i32, i32)
More information about the llvm-commits
mailing list