[llvm] [RISCV][GlobalISel] Legalize and select G_PREFETCH (PR #215466)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 10 23:20:05 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-globalisel

Author: Kane Wang (ReVe1uv)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/215466.diff


4 Files Affected:

- (modified) llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp (+46) 
- (modified) llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp (+2) 
- (modified) llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir (+2-2) 
- (added) llvm/test/CodeGen/RISCV/GlobalISel/prefetch.ll (+116) 


``````````diff
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
index 16af89c715861..9b830a2b6a70f 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 {
@@ -1444,11 +1445,56 @@ 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);
+  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;
+
+  Register AddrReg = MI.getOperand(0).getReg();
+  MachineInstr *Def = MRI->getVRegDef(AddrReg);
+
+  MachineOperand Base = MachineOperand::CreateReg(AddrReg, false);
+  int64_t Offset = 0;
+  if (Def && Def->getOpcode() == TargetOpcode::G_FRAME_INDEX) {
+    Base = Def->getOperand(1);
+  } else if (Def && isBaseWithConstantOffset(MI.getOperand(0), *MRI)) {
+    MachineOperand &BaseMO = Def->getOperand(1);
+    MachineInstr *RHSDef = MRI->getVRegDef(Def->getOperand(2).getReg());
+    int64_t C = RHSDef->getOperand(1).getCImm()->getSExtValue();
+    if (C % 32 == 0 && isInt<12>(C)) {
+      // Fold frame index + offset if the base is a G_FRAME_INDEX.
+      if (MachineInstr *BaseDef = MRI->getVRegDef(BaseMO.getReg());
+          BaseDef && BaseDef->getOpcode() == TargetOpcode::G_FRAME_INDEX)
+        Base = BaseDef->getOperand(1);
+      else
+        Base = BaseMO;
+      Offset = C;
+    }
+  }
+
+  MachineInstr *SelectedMI =
+      BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII.get(Opc))
+          .add(Base)
+          .addImm(Offset);
+  MI.eraseFromParent();
+  constrainSelectedInstRegOperands(*SelectedMI, 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)

``````````

</details>


https://github.com/llvm/llvm-project/pull/215466


More information about the llvm-commits mailing list