[llvm] [AMDGPU] Support global address in V/S_MOV_B64 lowering (PR #203527)
David Stuttard via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 12 06:29:46 PDT 2026
https://github.com/dstutt created https://github.com/llvm/llvm-project/pull/203527
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.
>From a97250094705665f1159f4567d6e8d3a4e70aaa2 Mon Sep 17 00:00:00 2001
From: David Stuttard <david.stuttard at amd.com>
Date: Fri, 12 Jun 2026 14:19:00 +0100
Subject: [PATCH] [AMDGPU] Support global address in V/S_MOV_B64 lowering
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.
---
llvm/lib/Target/AMDGPU/SIInstrInfo.cpp | 40 +++++++-
.../AMDGPU/expand-mov-b64-globaladdr.ll | 95 +++++++++++++++++++
2 files changed, 132 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/CodeGen/AMDGPU/expand-mov-b64-globaladdr.ll
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
+}
More information about the llvm-commits
mailing list