[llvm] [AMDGPU] Support global address in V/S_MOV_B64 lowering (PR #203527)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 12 06:30:35 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: David Stuttard (dstutt)

<details>
<summary>Changes</summary>

Hit an issue where V_MOV_B64_PSEUDO had a global, which wasn't previously handled.
Added support for this, and also for the S_MOV_B64_IMM_PSEUDO to make it symmetrical.

Claude has been used for this commit, primarily assisting creating a test.


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


2 Files Affected:

- (modified) llvm/lib/Target/AMDGPU/SIInstrInfo.cpp (+37-3) 
- (added) llvm/test/CodeGen/AMDGPU/expand-mov-b64-globaladdr.ll (+95) 


``````````diff
diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp
index a5198cce50e0f..94418f327f328 100644
--- a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp
@@ -2088,6 +2088,27 @@ bool SIInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
   MachineBasicBlock &MBB = *MI.getParent();
   DebugLoc DL = MBB.findDebugLoc(MI);
   const AMDGPU::LaneMaskConstants &LMC = AMDGPU::LaneMaskConstants::get(ST);
+
+  // Materialize the two halves of a 64-bit global address using absolute
+  // hi/lo relocations. Used to lower V_MOV_B64_PSEUDO / S_MOV_B64_IMM_PSEUDO
+  // when the source operand is a GlobalValue.
+  auto LowerMovB64GlobalAddress = [&](unsigned MovOpc) {
+    Register Dst = MI.getOperand(0).getReg();
+    Register DstLo = RI.getSubReg(Dst, AMDGPU::sub0);
+    Register DstHi = RI.getSubReg(Dst, AMDGPU::sub1);
+    const MachineOperand &SrcOp = MI.getOperand(1);
+    const GlobalValue *GV = SrcOp.getGlobal();
+    int64_t Offset = SrcOp.getOffset();
+    unsigned BaseFlags = SrcOp.getTargetFlags() &
+      ~(SIInstrInfo::MO_ABS32_LO | SIInstrInfo::MO_ABS32_HI);
+    BuildMI(MBB, MI, DL, get(MovOpc), DstLo)
+      .addGlobalAddress(GV, Offset, BaseFlags | SIInstrInfo::MO_ABS32_LO)
+      .addReg(Dst, RegState::Implicit | RegState::Define);
+    BuildMI(MBB, MI, DL, get(MovOpc), DstHi)
+      .addGlobalAddress(GV, Offset, BaseFlags | SIInstrInfo::MO_ABS32_HI)
+      .addReg(Dst, RegState::Implicit | RegState::Define);
+  };
+
   switch (MI.getOpcode()) {
   default: return TargetInstrInfo::expandPostRAPseudo(MI);
   case AMDGPU::S_MOV_B64_term:
@@ -2204,11 +2225,17 @@ bool SIInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
     assert(!SrcOp.isFPImm());
     if (ST.hasVMovB64Inst() && Mov64RC->contains(Dst)) {
       MI.setDesc(Mov64Desc);
-      if (SrcOp.isReg() || isInlineConstant(MI, 1) ||
-          isUInt<32>(SrcOp.getImm()) || ST.has64BitLiterals())
+      if (SrcOp.isReg() ||
+          isInlineConstant(MI, 1) ||
+          (SrcOp.isImm() && (isUInt<32>(SrcOp.getImm()) || ST.has64BitLiterals())) ||
+          (SrcOp.isGlobal() && ST.has64BitLiterals()))
         break;
     }
-    if (SrcOp.isImm()) {
+    if (SrcOp.isGlobal()) {
+      // The address is unknown until link time, so the PK_MOV inline-constant
+      // shortcut cannot apply.
+      LowerMovB64GlobalAddress(AMDGPU::V_MOV_B32_e32);
+    } else if (SrcOp.isImm()) {
       APInt Imm(64, SrcOp.getImm());
       APInt Lo(32, Imm.getLoBits(32).getZExtValue());
       APInt Hi(32, Imm.getHiBits(32).getZExtValue());
@@ -2270,6 +2297,13 @@ bool SIInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
       break;
     }
 
+    if (SrcOp.isGlobal()) {
+      LowerMovB64GlobalAddress(AMDGPU::S_MOV_B32);
+      MI.eraseFromParent();
+      break;
+    }
+
+    // SrcOp is immediate
     APInt Imm(64, SrcOp.getImm());
     if (Imm.isIntN(32) || isInlineConstant(Imm)) {
       MI.setDesc(get(AMDGPU::S_MOV_B64));
diff --git a/llvm/test/CodeGen/AMDGPU/expand-mov-b64-globaladdr.ll b/llvm/test/CodeGen/AMDGPU/expand-mov-b64-globaladdr.ll
new file mode 100644
index 0000000000000..f59b2d1f59847
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/expand-mov-b64-globaladdr.ll
@@ -0,0 +1,95 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=amdgcn--amdpal -mcpu=gfx1010 < %s | FileCheck --check-prefix=GFX10 %s
+; RUN: llc -mtriple=amdgcn--amdpal -mcpu=gfx1100 < %s | FileCheck --check-prefix=GFX11 %s
+
+; Verify that V_MOV_B64_PSEUDO and S_MOV_B64_IMM_PSEUDO are expanded into a
+; pair of 32-bit moves of the absolute hi/lo halves of a global address when
+; the subtarget does not support 64-bit literals.
+
+ at gv = external addrspace(4) constant [6 x i32]
+
+; Divergent select of @gv lowers to V_MOV_B64_PSEUDO with a GlobalAddress
+; operand which expandPostRAPseudo splits into v_mov_b32 abs32 at lo/@hi.
+define amdgpu_ps ptr addrspace(4) @v_mov_b64_pseudo_globaladdr(i1 %cond) {
+; GFX10-LABEL: v_mov_b64_pseudo_globaladdr:
+; GFX10:       ; %bb.0: ; %entry
+; GFX10-NEXT:    v_and_b32_e32 v2, 1, v0
+; GFX10-NEXT:    v_mov_b32_e32 v0, 0
+; GFX10-NEXT:    v_mov_b32_e32 v1, 0
+; GFX10-NEXT:    v_cmp_ne_u32_e32 vcc_lo, 1, v2
+; GFX10-NEXT:    s_and_saveexec_b32 s0, vcc_lo
+; GFX10-NEXT:  ; %bb.1: ; %other
+; GFX10-NEXT:    v_mov_b32_e32 v0, gv at abs32@lo
+; GFX10-NEXT:    v_mov_b32_e32 v1, gv at abs32@hi
+; GFX10-NEXT:  ; %bb.2: ; %join
+; GFX10-NEXT:    s_or_b32 exec_lo, exec_lo, s0
+; GFX10-NEXT:    v_readfirstlane_b32 s0, v0
+; GFX10-NEXT:    v_readfirstlane_b32 s1, v1
+; GFX10-NEXT:    ; return to shader part epilog
+;
+; GFX11-LABEL: v_mov_b64_pseudo_globaladdr:
+; GFX11:       ; %bb.0: ; %entry
+; GFX11-NEXT:    v_dual_mov_b32 v1, 0 :: v_dual_and_b32 v2, 1, v0
+; GFX11-NEXT:    v_mov_b32_e32 v0, 0
+; GFX11-NEXT:    s_mov_b32 s0, exec_lo
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2)
+; GFX11-NEXT:    v_cmpx_ne_u32_e32 1, v2
+; GFX11-NEXT:  ; %bb.1: ; %other
+; GFX11-NEXT:    v_mov_b32_e32 v0, gv at abs32@lo
+; GFX11-NEXT:    v_mov_b32_e32 v1, gv at abs32@hi
+; GFX11-NEXT:  ; %bb.2: ; %join
+; GFX11-NEXT:    s_or_b32 exec_lo, exec_lo, s0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX11-NEXT:    v_readfirstlane_b32 s0, v0
+; GFX11-NEXT:    v_readfirstlane_b32 s1, v1
+; GFX11-NEXT:    ; return to shader part epilog
+entry:
+  br i1 %cond, label %join, label %other
+
+other:
+  br label %join
+
+join:
+  %sink = phi ptr addrspace(4) [ null, %entry ], [ @gv, %other ]
+  ret ptr addrspace(4) %sink
+}
+
+; Uniform select of @gv lowers to S_MOV_B64_IMM_PSEUDO with a GlobalAddress
+; operand which expandPostRAPseudo splits into s_mov_b32 abs32 at lo/@hi.
+define amdgpu_ps ptr addrspace(4) @s_mov_b64_imm_pseudo_globaladdr(i1 inreg %cond) {
+; GFX10-LABEL: s_mov_b64_imm_pseudo_globaladdr:
+; GFX10:       ; %bb.0: ; %entry
+; GFX10-NEXT:    s_bitcmp1_b32 s0, 0
+; GFX10-NEXT:    s_cselect_b32 s0, -1, 0
+; GFX10-NEXT:    s_and_b32 vcc_lo, exec_lo, s0
+; GFX10-NEXT:    s_mov_b64 s[0:1], 0
+; GFX10-NEXT:    s_cbranch_vccnz .LBB1_2
+; GFX10-NEXT:  ; %bb.1: ; %other
+; GFX10-NEXT:    s_mov_b32 s1, gv at abs32@hi
+; GFX10-NEXT:    s_mov_b32 s0, gv at abs32@lo
+; GFX10-NEXT:  .LBB1_2: ; %join
+; GFX10-NEXT:    ; return to shader part epilog
+;
+; GFX11-LABEL: s_mov_b64_imm_pseudo_globaladdr:
+; GFX11:       ; %bb.0: ; %entry
+; GFX11-NEXT:    s_bitcmp1_b32 s0, 0
+; GFX11-NEXT:    s_cselect_b32 s0, -1, 0
+; GFX11-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
+; GFX11-NEXT:    s_and_b32 vcc_lo, exec_lo, s0
+; GFX11-NEXT:    s_mov_b64 s[0:1], 0
+; GFX11-NEXT:    s_cbranch_vccnz .LBB1_2
+; GFX11-NEXT:  ; %bb.1: ; %other
+; GFX11-NEXT:    s_mov_b32 s1, gv at abs32@hi
+; GFX11-NEXT:    s_mov_b32 s0, gv at abs32@lo
+; GFX11-NEXT:  .LBB1_2: ; %join
+; GFX11-NEXT:    ; return to shader part epilog
+entry:
+  br i1 %cond, label %join, label %other
+
+other:
+  br label %join
+
+join:
+  %sink = phi ptr addrspace(4) [ null, %entry ], [ @gv, %other ]
+  ret ptr addrspace(4) %sink
+}

``````````

</details>


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


More information about the llvm-commits mailing list