Skip to content

Commit e4308c7

Browse files
gandhi56cursoragent
andcommitted
[AMDGPU][GlobalISel] Fold trivial uniform G_AMDGPU_READANYLANE
Eliminate redundant G_AMDGPU_READANYLANE (selected as v_readfirstlane_b32) that only round-trips a uniform value through the VGPR/SGPR banks. Two complementary changes: - AMDGPURegBankCombiner: add a combine that replaces a G_AMDGPU_READANYLANE whose source is already uniform (an sgpr value merely copied into a vgpr) with the original sgpr value. - AMDGPURegBankLegalize: extend tryMatchMergeReadAnyLane to handle merges with mixed sources. When at least one source is a readanylane, the merge is rebuilt entirely in the vgpr bank, using each readanylane's vgpr operand directly and copying the plain uniform sources into vgpr. This removes the sgpr -> vgpr -> sgpr -> vgpr round trip for patterns like build_vector(readanylane(x), uniform_y) feeding a vector ALU op. Regenerate affected check lines in packed-fp32.ll and mul.ll and add a MIR test for the new combine. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 2b6e9d2 commit e4308c7

6 files changed

Lines changed: 189 additions & 159 deletions

File tree

llvm/lib/Target/AMDGPU/AMDGPUCombine.td

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,14 @@ class minmax_to_minmax3_opcodes<Instruction minmaxOpcode> : GICombineRule<
173173
[{ return matchMinMaxToMinMax3(*${min_or_max}, ${matchinfo}); }]),
174174
(apply [{ applyMinMaxToMinMax3(*${min_or_max}, ${matchinfo}); }])>;
175175

176+
// Fold a redundant G_AMDGPU_READANYLANE (v_readfirstlane_b32) whose source is
177+
// already uniform, i.e. an sgpr -> vgpr -> sgpr round trip.
178+
def redundant_readanylane : GICombineRule<
179+
(defs root:$readanylane, register_matchinfo:$matchinfo),
180+
(match (wip_match_opcode G_AMDGPU_READANYLANE):$readanylane,
181+
[{ return matchRedundantReadAnyLane(*${readanylane}, ${matchinfo}); }]),
182+
(apply [{ Helper.replaceSingleDefInstWithReg(*${readanylane}, ${matchinfo}); }])>;
183+
176184
def smax_to_minmax3 : minmax_to_minmax3_opcodes<G_SMAX>;
177185
def smin_to_minmax3 : minmax_to_minmax3_opcodes<G_SMIN>;
178186
def umax_to_minmax3 : minmax_to_minmax3_opcodes<G_UMAX>;
@@ -270,5 +278,6 @@ def AMDGPURegBankCombiner : GICombiner<
270278
cast_of_cast_combines, sext_trunc, zext_of_shift_amount_combines,
271279
d16_load, smax_to_minmax3, smin_to_minmax3, umax_to_minmax3,
272280
umin_to_minmax3, fmax_to_minmax3, fmin_to_minmax3, fmaximum_to_minmax3,
273-
fminimum_to_minmax3, fmax_ieee_to_minmax3, fmin_ieee_to_minmax3]> {
281+
fminimum_to_minmax3, fmax_ieee_to_minmax3, fmin_ieee_to_minmax3,
282+
redundant_readanylane]> {
274283
}

llvm/lib/Target/AMDGPU/AMDGPURegBankCombiner.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
2424
#include "llvm/CodeGen/GlobalISel/GISelValueTracking.h"
2525
#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"
26+
#include "llvm/CodeGen/GlobalISel/Utils.h"
2627
#include "llvm/CodeGen/MachineDominators.h"
2728
#include "llvm/CodeGen/TargetPassConfig.h"
2829
#include "llvm/Target/TargetMachine.h"
@@ -103,6 +104,8 @@ class AMDGPURegBankCombinerImpl : public Combiner {
103104
void applyMinMaxToMinMax3(MachineInstr &MI,
104105
MinMaxToMinMax3MatchInfo &MatchInfo) const;
105106

107+
bool matchRedundantReadAnyLane(MachineInstr &MI, Register &Match) const;
108+
106109
private:
107110
SIModeRegisterDefaults getMode() const;
108111
bool getIEEE() const;
@@ -563,6 +566,36 @@ bool AMDGPURegBankCombinerImpl::matchMinMaxToMinMax3(
563566
return true;
564567
}
565568

569+
// Reading any lane of a value that is uniform across all lanes returns that
570+
// same value. G_AMDGPU_READANYLANE (selected as v_readfirstlane_b32) is emitted
571+
// by RegBankLegalize to move a uniform value from a vgpr into an sgpr. When the
572+
// source is itself already uniform (an sgpr value that was merely copied into a
573+
// vgpr), the read is a redundant sgpr -> vgpr -> sgpr round trip and the result
574+
// can be replaced with the original sgpr value.
575+
bool AMDGPURegBankCombinerImpl::matchRedundantReadAnyLane(
576+
MachineInstr &MI, Register &Match) const {
577+
assert(MI.getOpcode() == AMDGPU::G_AMDGPU_READANYLANE);
578+
Register Dst = MI.getOperand(0).getReg();
579+
Register Src = MI.getOperand(1).getReg();
580+
581+
// Look through copies to the value actually being read.
582+
Register SrcNoCopy = getSrcRegIgnoringCopies(Src, MRI);
583+
if (!SrcNoCopy.isValid())
584+
return false;
585+
586+
// Only fold when the read value is uniform (lives in the sgpr bank).
587+
const RegisterBank *RB = MRI.getRegBankOrNull(SrcNoCopy);
588+
if (!RB || RB->getID() != AMDGPU::SGPRRegBankID)
589+
return false;
590+
591+
// Copies preserve the type, but be defensive so we never change it.
592+
if (MRI.getType(SrcNoCopy) != MRI.getType(Dst))
593+
return false;
594+
595+
Match = SrcNoCopy;
596+
return true;
597+
}
598+
566599
bool AMDGPURegBankCombinerImpl::applyD16Load(
567600
unsigned D16Opc, MachineInstr &DstMI, MachineInstr *SmallLoad,
568601
Register SrcReg32ToOverwriteD16) const {

llvm/lib/Target/AMDGPU/AMDGPURegBankLegalize.cpp

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -172,19 +172,40 @@ Register AMDGPURegBankLegalizeCombiner::tryMatchUnmergeDefs(
172172
return UnMerge->getSourceReg();
173173
}
174174

175-
// Check if all merge sources are readanylanes and return the readanylane
176-
// sources if they are.
175+
// Return the vgpr sources for a merge that can be rebuilt entirely in the vgpr
176+
// bank. A source is either the vgpr operand of a readanylane, or, for a plain
177+
// uniform (sgpr) source, a fresh vgpr copy of it. This is only worthwhile (and
178+
// only returns a non-empty result) when at least one source is a readanylane,
179+
// so rebuilding the merge in vgpr removes that vgpr -> sgpr read.
177180
SmallVector<Register> AMDGPURegBankLegalizeCombiner::tryMatchMergeReadAnyLane(
178181
GMergeLikeInstr *Merge) {
179-
SmallVector<Register> ReadAnyLaneSrcs;
180-
for (unsigned i = 0; i < Merge->getNumSources(); ++i) {
182+
SmallVector<Register> Srcs;
183+
SmallVector<bool> NeedsCopyToVgpr;
184+
bool FoundReadAnyLane = false;
185+
for (unsigned I = 0; I < Merge->getNumSources(); ++I) {
181186
Register Src;
182-
if (!mi_match(Merge->getSourceReg(i), MRI,
183-
m_GAMDGPUReadAnyLane(m_Reg(Src))))
184-
return {};
185-
ReadAnyLaneSrcs.push_back(Src);
187+
if (mi_match(Merge->getSourceReg(I), MRI,
188+
m_GAMDGPUReadAnyLane(m_Reg(Src)))) {
189+
Srcs.push_back(Src);
190+
NeedsCopyToVgpr.push_back(false);
191+
FoundReadAnyLane = true;
192+
continue;
193+
}
194+
Srcs.push_back(Merge->getSourceReg(I));
195+
NeedsCopyToVgpr.push_back(true);
196+
}
197+
198+
if (!FoundReadAnyLane)
199+
return {};
200+
201+
// Materialize vgpr copies for the uniform (non-readanylane) sources so the
202+
// whole merge can live in the vgpr bank.
203+
for (unsigned I = 0; I < Srcs.size(); ++I) {
204+
if (NeedsCopyToVgpr[I])
205+
Srcs[I] = B.buildCopy({VgprRB, MRI.getType(Srcs[I])}, Srcs[I]).getReg(0);
186206
}
187-
return ReadAnyLaneSrcs;
207+
208+
return Srcs;
188209
}
189210

190211
SmallVector<Register>
@@ -402,8 +423,8 @@ void AMDGPURegBankLegalizeCombiner::tryCombineS1AnyExt(MachineInstr &MI) {
402423
// Search through MRI for virtual registers with sgpr register bank and S1 LLT.
403424
[[maybe_unused]] static Register getAnySgprS1(const MachineRegisterInfo &MRI) {
404425
const LLT S1 = LLT::scalar(1);
405-
for (unsigned i = 0; i < MRI.getNumVirtRegs(); ++i) {
406-
Register Reg = Register::index2VirtReg(i);
426+
for (unsigned I = 0; I < MRI.getNumVirtRegs(); ++I) {
427+
Register Reg = Register::index2VirtReg(I);
407428
if (MRI.def_empty(Reg) || MRI.getType(Reg) != S1)
408429
continue;
409430

llvm/test/CodeGen/AMDGPU/GlobalISel/mul.ll

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3752,15 +3752,13 @@ define amdgpu_kernel void @s_mul_u64_zext_with_sregs(ptr addrspace(1) %out, ptr
37523752
; GFX7-NEXT: s_load_dwordx4 s[0:3], s[4:5], 0x9
37533753
; GFX7-NEXT: v_mov_b32_e32 v0, 0x50
37543754
; GFX7-NEXT: s_waitcnt lgkmcnt(0)
3755-
; GFX7-NEXT: s_load_dword s3, s[2:3], 0x0
3756-
; GFX7-NEXT: s_mov_b32 s2, -1
3755+
; GFX7-NEXT: s_load_dword s2, s[2:3], 0x0
37573756
; GFX7-NEXT: s_waitcnt lgkmcnt(0)
3758-
; GFX7-NEXT: v_mul_hi_u32 v0, s3, v0
3759-
; GFX7-NEXT: s_mul_i32 s4, s3, 0x50
3757+
; GFX7-NEXT: v_mul_hi_u32 v1, s2, v0
3758+
; GFX7-NEXT: s_mul_i32 s3, s2, 0x50
3759+
; GFX7-NEXT: v_mov_b32_e32 v0, s3
3760+
; GFX7-NEXT: s_mov_b32 s2, -1
37603761
; GFX7-NEXT: s_mov_b32 s3, 0xf000
3761-
; GFX7-NEXT: v_readfirstlane_b32 s5, v0
3762-
; GFX7-NEXT: v_mov_b32_e32 v0, s4
3763-
; GFX7-NEXT: v_mov_b32_e32 v1, s5
37643762
; GFX7-NEXT: buffer_store_dwordx2 v[0:1], off, s[0:3], 0
37653763
; GFX7-NEXT: s_endpgm
37663764
;
@@ -3773,11 +3771,9 @@ define amdgpu_kernel void @s_mul_u64_zext_with_sregs(ptr addrspace(1) %out, ptr
37733771
; GFX8-NEXT: v_mov_b32_e32 v3, s1
37743772
; GFX8-NEXT: v_mov_b32_e32 v2, s0
37753773
; GFX8-NEXT: s_waitcnt lgkmcnt(0)
3776-
; GFX8-NEXT: v_mul_hi_u32 v0, s2, v0
3774+
; GFX8-NEXT: v_mul_hi_u32 v1, s2, v0
37773775
; GFX8-NEXT: s_mulk_i32 s2, 0x50
3778-
; GFX8-NEXT: v_readfirstlane_b32 s3, v0
37793776
; GFX8-NEXT: v_mov_b32_e32 v0, s2
3780-
; GFX8-NEXT: v_mov_b32_e32 v1, s3
37813777
; GFX8-NEXT: flat_store_dwordx2 v[2:3], v[0:1]
37823778
; GFX8-NEXT: s_endpgm
37833779
;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
2+
# RUN: llc -mtriple=amdgcn -mcpu=gfx1100 -run-pass=amdgpu-regbank-combiner %s -o - | FileCheck %s
3+
4+
# A G_AMDGPU_READANYLANE whose source is a uniform (sgpr) value copied into a
5+
# vgpr is a redundant sgpr -> vgpr -> sgpr round trip and should be folded away.
6+
---
7+
name: readanylane_of_copy_from_sgpr
8+
tracksRegLiveness: true
9+
body: |
10+
bb.0:
11+
liveins: $sgpr0
12+
; CHECK-LABEL: name: readanylane_of_copy_from_sgpr
13+
; CHECK: liveins: $sgpr0
14+
; CHECK-NEXT: {{ $}}
15+
; CHECK-NEXT: [[COPY:%[0-9]+]]:sgpr(s32) = COPY $sgpr0
16+
; CHECK-NEXT: $sgpr0 = COPY [[COPY]](s32)
17+
; CHECK-NEXT: S_ENDPGM 0
18+
%0:sgpr(s32) = COPY $sgpr0
19+
%1:vgpr(s32) = COPY %0
20+
%2:sgpr(s32) = G_AMDGPU_READANYLANE %1
21+
$sgpr0 = COPY %2(s32)
22+
S_ENDPGM 0
23+
...
24+
25+
# Negative test: the source is a genuine vgpr (divergent) value, so the read is
26+
# required and must not be folded.
27+
---
28+
name: readanylane_of_divergent
29+
tracksRegLiveness: true
30+
body: |
31+
bb.0:
32+
liveins: $vgpr0, $vgpr1
33+
; CHECK-LABEL: name: readanylane_of_divergent
34+
; CHECK: liveins: $vgpr0, $vgpr1
35+
; CHECK-NEXT: {{ $}}
36+
; CHECK-NEXT: [[COPY:%[0-9]+]]:vgpr(s32) = COPY $vgpr0
37+
; CHECK-NEXT: [[COPY1:%[0-9]+]]:vgpr(s32) = COPY $vgpr1
38+
; CHECK-NEXT: [[ADD:%[0-9]+]]:vgpr(s32) = G_ADD [[COPY]], [[COPY1]]
39+
; CHECK-NEXT: [[READ:%[0-9]+]]:sgpr(s32) = G_AMDGPU_READANYLANE [[ADD]]
40+
; CHECK-NEXT: $sgpr0 = COPY [[READ]](s32)
41+
; CHECK-NEXT: S_ENDPGM 0
42+
%0:vgpr(s32) = COPY $vgpr0
43+
%1:vgpr(s32) = COPY $vgpr1
44+
%2:vgpr(s32) = G_ADD %0, %1
45+
%3:sgpr(s32) = G_AMDGPU_READANYLANE %2
46+
$sgpr0 = COPY %3(s32)
47+
S_ENDPGM 0
48+
...

0 commit comments

Comments
 (0)