[llvm] [AMDGPU] Detect illegal 64 relocs in SIFold (PR #219141)

David Stuttard via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 27 01:17:23 PDT 2026


https://github.com/dstutt created https://github.com/llvm/llvm-project/pull/219141

Had some cases where operand folding was missing that a reloc
might be 64 bit and thus can't be folded into a VOP3/VOP3P/VOPD
instructions even if the target supports 64 bit literals.
Enhanced verifier to pick up violations as well.

Also updated target-flags.mir test to use a specific variant due to the
enhanced verifier checks.


>From 48e2b33eeb4a69cf3b48082991593ca4d54b1653 Mon Sep 17 00:00:00 2001
From: David Stuttard <david.stuttard at amd.com>
Date: Wed, 26 Aug 2026 14:18:24 +0100
Subject: [PATCH] [AMDGPU] Detect illegal 64 relocs in SIFold

Had some cases where operand folding was missing that a reloc
might be 64 bit and thus can't be folded into a VOP3/VOP3P/VOPD
instructions even if the target supports 64 bit literals.
Enhanced verifier to pick up violations as well.

Also updated target-flags.mir test to use a specific variant due to the
enhanced verifier checks.
---
 llvm/lib/Target/AMDGPU/SIInstrInfo.cpp        | 31 ++++++++++
 llvm/test/CodeGen/AMDGPU/literal64-symbol.ll  | 57 +++++++++++++++++++
 llvm/test/CodeGen/MIR/AMDGPU/target-flags.mir |  2 +-
 3 files changed, 89 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/literal64-symbol.ll

diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp
index bc419d813f171..c7ecac4de8b6f 100644
--- a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp
@@ -127,6 +127,17 @@ static bool canRemat(const MachineInstr &MI) {
   return false;
 }
 
+static bool is64BitReloc(unsigned TargetFlags) {
+  switch (TargetFlags & SIInstrInfo::MO_MASK) {
+  case SIInstrInfo::MO_ABS64:
+  case SIInstrInfo::MO_REL64:
+  case SIInstrInfo::MO_GOTPCREL64:
+    return true;
+  default:
+    return false;
+  }
+}
+
 // Split relocation flags for 64-bit global-address materialization into a
 // common base and the hi/lo relocation variants.
 static std::tuple<unsigned, unsigned, unsigned>
@@ -5431,6 +5442,18 @@ bool SIInstrInfo::verifyInstruction(const MachineInstr &MI,
         ErrInfo = "illegal 64-bit immediate value for operand.";
         return false;
       }
+      // A global with a 64-bit relocation resolves to a 64-bit literal at link
+      // time. It cannot be encoded without 64-bit literal support, nor in
+      // 8-byte encodings (e.g. VOP3, VOP3P, VOPD) which cannot carry one.
+      // V_MOV_B64_PSEUDO and S_MOV_B64_IMM_PSEUDO are exempt: they split the
+      // relocation into lo/hi halves when expanded in expandPostRAPseudo.
+      if (MO.isGlobal() && is64BitReloc(MO.getTargetFlags()) &&
+          MI.getOpcode() != AMDGPU::V_MOV_B64_PSEUDO &&
+          MI.getOpcode() != AMDGPU::S_MOV_B64_IMM_PSEUDO &&
+          (!ST.has64BitLiterals() || Desc.getSize() != 4)) {
+        ErrInfo = "illegal 64-bit relocation for operand.";
+        return false;
+      }
       break;
     case AMDGPU::OPERAND_INLINE_SPLIT_BARRIER_INT32:
     case AMDGPU::OPERAND_INPUT_MODS:
@@ -6781,6 +6804,14 @@ bool SIInstrInfo::isOperandLegal(const MachineInstr &MI, unsigned OpIdx,
   // Handle non-register types that are treated like immediates.
   assert(MO->isImm() || MO->isTargetIndex() || MO->isFI() || MO->isGlobal());
 
+  // A global address with a 64-bit relocation resolves to a 64-bit literal at
+  // link time. The 64-bit literal encoding is only available on targets with
+  // has64BitLiterals(), and even then only for single-DWORD (4-byte) base
+  // instructions.
+  if (MO->isGlobal() && is64BitReloc(MO->getTargetFlags()) &&
+      (!ST.has64BitLiterals() || InstDesc.getSize() != 4))
+    return false;
+
   if (!DefinedRC) {
     // This operand expects an immediate.
     return true;
diff --git a/llvm/test/CodeGen/AMDGPU/literal64-symbol.ll b/llvm/test/CodeGen/AMDGPU/literal64-symbol.ll
new file mode 100644
index 0000000000000..83e305ea6b3d7
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/literal64-symbol.ll
@@ -0,0 +1,57 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 3
+; RUN: llc -mtriple=amdgpu12.00 < %s | FileCheck %s --check-prefixes=GFX12
+; RUN: llc -mtriple=amdgpu12.50 < %s | FileCheck %s --check-prefixes=GFX1250
+; RUN: llc -mtriple=amdgpu13.10 < %s | FileCheck %s --check-prefixes=GFX13
+
+ at sym = external addrspace(4) constant [2 x <3 x i32>]
+
+; An MO_ABS64 global must not be folded into a VOP3 (here v_mad_co_u64_u32) as a
+; 64-bit literal: no VOP3 encoding can carry one even when 64-bit literals are
+; supported. It must be materialized into an SGPR first.
+define amdgpu_gs ptr addrspace(4) @v_mad_symbol(i32 %idx) {
+; GFX12-LABEL: v_mad_symbol:
+; GFX12:       ; %bb.0:
+; GFX12-NEXT:    s_getpc_b64 s[0:1]
+; GFX12-NEXT:    s_sext_i32_i16 s1, s1
+; GFX12-NEXT:    s_add_co_u32 s0, s0, sym at gotpcrel32@lo+8
+; GFX12-NEXT:    s_add_co_ci_u32 s1, s1, sym at gotpcrel32@hi+16
+; GFX12-NEXT:    s_load_b64 s[0:1], s[0:1], 0x0
+; GFX12-NEXT:    s_wait_kmcnt 0x0
+; GFX12-NEXT:    v_mad_co_u64_u32 v[0:1], null, v0, 24, s[0:1]
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX12-NEXT:    v_readfirstlane_b32 s0, v0
+; GFX12-NEXT:    v_readfirstlane_b32 s1, v1
+; GFX12-NEXT:    s_wait_alu depctr_va_sdst(0)
+; GFX12-NEXT:    ; return to shader part epilog
+;
+; GFX1250-LABEL: v_mad_symbol:
+; GFX1250:       ; %bb.0:
+; GFX1250-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; GFX1250-NEXT:    s_mov_b64 s[64:65], 0
+; GFX1250-NEXT:    v_nop
+; GFX1250-NEXT:    global_prefetch_b8 v0, s[64:65] scope:SCOPE_SE
+; GFX1250-NEXT:    s_get_pc_i64 s[0:1]
+; GFX1250-NEXT:    s_add_nc_u64 s[0:1], s[0:1], sym at gotpcrel+4
+; GFX1250-NEXT:    s_load_b64 s[0:1], s[0:1], 0x0 nv
+; GFX1250-NEXT:    s_wait_kmcnt 0x0
+; GFX1250-NEXT:    v_mad_nc_u64_u32 v[0:1], v0, 24, s[0:1]
+; GFX1250-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX1250-NEXT:    v_readfirstlane_b32 s0, v0
+; GFX1250-NEXT:    v_readfirstlane_b32 s1, v1
+; GFX1250-NEXT:    ; return to shader part epilog
+;
+; GFX13-LABEL: v_mad_symbol:
+; GFX13:       ; %bb.0:
+; GFX13-NEXT:    s_get_pc_i64 s[0:1]
+; GFX13-NEXT:    s_add_nc_u64 s[0:1], s[0:1], sym at gotpcrel+4
+; GFX13-NEXT:    s_load_b64 s[0:1], s[0:1], 0x0 nv
+; GFX13-NEXT:    s_wait_kmcnt 0x0
+; GFX13-NEXT:    v_mad_co_u64_u32 v[0:1], null, v0, 24, s[0:1]
+; GFX13-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX13-NEXT:    v_readfirstlane_b32 s0, v0
+; GFX13-NEXT:    v_readfirstlane_b32 s1, v1
+; GFX13-NEXT:    ; return to shader part epilog
+  %ext = zext i32 %idx to i64
+  %gep = getelementptr [24 x i8], ptr addrspace(4) @sym, i64 %ext
+  ret ptr addrspace(4) %gep
+}
diff --git a/llvm/test/CodeGen/MIR/AMDGPU/target-flags.mir b/llvm/test/CodeGen/MIR/AMDGPU/target-flags.mir
index 3bce5400e576b..db63c7ecde6c1 100644
--- a/llvm/test/CodeGen/MIR/AMDGPU/target-flags.mir
+++ b/llvm/test/CodeGen/MIR/AMDGPU/target-flags.mir
@@ -1,5 +1,5 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple=amdgpu6.00 -run-pass none -o - %s | FileCheck %s
+# RUN: llc -mtriple=amdgpu12.50 -run-pass none -o - %s | FileCheck %s
 --- |
   define amdgpu_kernel void @flags() {
     ret void



More information about the llvm-commits mailing list