[llvm] [AMDGPU] Reject image load merges with mismatched dim (PR #219923)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 31 02:31:44 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: Arseniy Obolenskiy (aobolensk)

<details>
<summary>Changes</summary>

dim was missing from the operand equality check, so loads with different dim could wrongly get merged into one

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


5 Files Affected:

- (modified) llvm/lib/Target/AMDGPU/SILoadStoreOptimizer.cpp (+2-1) 
- (added) llvm/test/CodeGen/AMDGPU/merge-image-load-dim.ll (+90) 
- (modified) llvm/test/CodeGen/AMDGPU/merge-image-load-gfx10.mir (+18) 
- (modified) llvm/test/CodeGen/AMDGPU/merge-image-load-gfx11.mir (+18) 
- (modified) llvm/test/CodeGen/AMDGPU/merge-image-load-gfx12.mir (+20) 


``````````diff
diff --git a/llvm/lib/Target/AMDGPU/SILoadStoreOptimizer.cpp b/llvm/lib/Target/AMDGPU/SILoadStoreOptimizer.cpp
index d86a3c62f63c3..e8fd711505af5 100644
--- a/llvm/lib/Target/AMDGPU/SILoadStoreOptimizer.cpp
+++ b/llvm/lib/Target/AMDGPU/SILoadStoreOptimizer.cpp
@@ -1022,7 +1022,8 @@ bool SILoadStoreOptimizer::dmasksCanBeCombined(const CombineInfo &CI,
   // Check other optional immediate operands for equality.
   AMDGPU::OpName OperandsToMatch[] = {
       AMDGPU::OpName::cpol, AMDGPU::OpName::d16,  AMDGPU::OpName::unorm,
-      AMDGPU::OpName::da,   AMDGPU::OpName::r128, AMDGPU::OpName::a16};
+      AMDGPU::OpName::da,   AMDGPU::OpName::r128, AMDGPU::OpName::a16,
+      AMDGPU::OpName::dim};
 
   for (AMDGPU::OpName op : OperandsToMatch) {
     int Idx = AMDGPU::getNamedOperandIdx(CI.I->getOpcode(), op);
diff --git a/llvm/test/CodeGen/AMDGPU/merge-image-load-dim.ll b/llvm/test/CodeGen/AMDGPU/merge-image-load-dim.ll
new file mode 100644
index 0000000000000..223c702402f39
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/merge-image-load-dim.ll
@@ -0,0 +1,90 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=amdgpu10.10 < %s | FileCheck -check-prefix=GFX10 %s
+; RUN: llc -mtriple=amdgpu11.00 -amdgpu-enable-delay-alu=0 < %s | FileCheck -check-prefix=GFX11 %s
+; RUN: llc -mtriple=amdgpu12.00 -amdgpu-enable-delay-alu=0 < %s | FileCheck -check-prefix=GFX12 %s
+
+; Mismatched dim must block merging: would reinterpret the array slice as y.
+
+define amdgpu_ps <4 x float> @load_2d_1darray_dim_mismatch(<8 x i32> inreg %rsrc, i32 %s, i32 %t) {
+; GFX10-LABEL: load_2d_1darray_dim_mismatch:
+; GFX10:       ; %bb.0: ; %main_body
+; GFX10-NEXT:    v_mov_b32_e32 v4, v1
+; GFX10-NEXT:    s_clause 0x1
+; GFX10-NEXT:    image_load v5, v[0:1], s[0:7] dmask:0x1 dim:SQ_RSRC_IMG_2D unorm
+; GFX10-NEXT:    image_load v[1:3], v[0:1], s[0:7] dmask:0xe dim:SQ_RSRC_IMG_1D_ARRAY unorm
+; GFX10-NEXT:    ; kill: killed $vgpr4
+; GFX10-NEXT:    s_waitcnt vmcnt(1)
+; GFX10-NEXT:    v_mov_b32_e32 v0, v5
+; GFX10-NEXT:    s_waitcnt vmcnt(0)
+; GFX10-NEXT:    ; return to shader part epilog
+;
+; GFX11-LABEL: load_2d_1darray_dim_mismatch:
+; GFX11:       ; %bb.0: ; %main_body
+; GFX11-NEXT:    s_clause 0x1
+; GFX11-NEXT:    image_load v4, v[0:1], s[0:7] dmask:0x1 dim:SQ_RSRC_IMG_2D unorm
+; GFX11-NEXT:    image_load v[1:3], v[0:1], s[0:7] dmask:0xe dim:SQ_RSRC_IMG_1D_ARRAY unorm
+; GFX11-NEXT:    s_waitcnt vmcnt(1)
+; GFX11-NEXT:    v_mov_b32_e32 v0, v4
+; GFX11-NEXT:    s_waitcnt vmcnt(0)
+; GFX11-NEXT:    ; return to shader part epilog
+;
+; GFX12-LABEL: load_2d_1darray_dim_mismatch:
+; GFX12:       ; %bb.0: ; %main_body
+; GFX12-NEXT:    s_clause 0x1
+; GFX12-NEXT:    image_load v4, [v0, v1], s[0:7] dmask:0x1 dim:SQ_RSRC_IMG_2D
+; GFX12-NEXT:    image_load v[1:3], [v0, v1], s[0:7] dmask:0xe dim:SQ_RSRC_IMG_1D_ARRAY
+; GFX12-NEXT:    s_wait_loadcnt 0x1
+; GFX12-NEXT:    v_mov_b32_e32 v0, v4
+; GFX12-NEXT:    s_wait_loadcnt 0x0
+; GFX12-NEXT:    ; return to shader part epilog
+main_body:
+  %v1 = call float @llvm.amdgcn.image.load.2d.f32.i32(i32 1, i32 %s, i32 %t, <8 x i32> %rsrc, i32 0, i32 0)
+  %v2 = call <3 x float> @llvm.amdgcn.image.load.1darray.v3f32.i32(i32 14, i32 %s, i32 %t, <8 x i32> %rsrc, i32 0, i32 0)
+  %e0 = extractelement <3 x float> %v2, i32 0
+  %e1 = extractelement <3 x float> %v2, i32 1
+  %e2 = extractelement <3 x float> %v2, i32 2
+  %r0 = insertelement <4 x float> poison, float %v1, i32 0
+  %r1 = insertelement <4 x float> %r0, float %e0, i32 1
+  %r2 = insertelement <4 x float> %r1, float %e1, i32 2
+  %r3 = insertelement <4 x float> %r2, float %e2, i32 3
+  ret <4 x float> %r3
+}
+
+; Matching dim: merging still expected.
+
+define amdgpu_ps <4 x float> @load_2d_2d_merged(<8 x i32> inreg %rsrc, i32 %s, i32 %t) {
+; GFX10-LABEL: load_2d_2d_merged:
+; GFX10:       ; %bb.0: ; %main_body
+; GFX10-NEXT:    image_load v[0:3], v[0:1], s[0:7] dmask:0xf dim:SQ_RSRC_IMG_2D unorm
+; GFX10-NEXT:    s_waitcnt vmcnt(0)
+; GFX10-NEXT:    ; return to shader part epilog
+;
+; GFX11-LABEL: load_2d_2d_merged:
+; GFX11:       ; %bb.0: ; %main_body
+; GFX11-NEXT:    image_load v[0:3], v[0:1], s[0:7] dmask:0xf dim:SQ_RSRC_IMG_2D unorm
+; GFX11-NEXT:    s_waitcnt vmcnt(0)
+; GFX11-NEXT:    ; return to shader part epilog
+;
+; GFX12-LABEL: load_2d_2d_merged:
+; GFX12:       ; %bb.0: ; %main_body
+; GFX12-NEXT:    image_load v[0:3], [v0, v1], s[0:7] dmask:0xf dim:SQ_RSRC_IMG_2D
+; GFX12-NEXT:    s_wait_loadcnt 0x0
+; GFX12-NEXT:    ; return to shader part epilog
+main_body:
+  %v1 = call float @llvm.amdgcn.image.load.2d.f32.i32(i32 1, i32 %s, i32 %t, <8 x i32> %rsrc, i32 0, i32 0)
+  %v2 = call <3 x float> @llvm.amdgcn.image.load.2d.v3f32.i32(i32 14, i32 %s, i32 %t, <8 x i32> %rsrc, i32 0, i32 0)
+  %e0 = extractelement <3 x float> %v2, i32 0
+  %e1 = extractelement <3 x float> %v2, i32 1
+  %e2 = extractelement <3 x float> %v2, i32 2
+  %r0 = insertelement <4 x float> poison, float %v1, i32 0
+  %r1 = insertelement <4 x float> %r0, float %e0, i32 1
+  %r2 = insertelement <4 x float> %r1, float %e1, i32 2
+  %r3 = insertelement <4 x float> %r2, float %e2, i32 3
+  ret <4 x float> %r3
+}
+
+declare float @llvm.amdgcn.image.load.2d.f32.i32(i32, i32, i32, <8 x i32>, i32, i32) #0
+declare <3 x float> @llvm.amdgcn.image.load.2d.v3f32.i32(i32, i32, i32, <8 x i32>, i32, i32) #0
+declare <3 x float> @llvm.amdgcn.image.load.1darray.v3f32.i32(i32, i32, i32, <8 x i32>, i32, i32) #0
+
+attributes #0 = { nounwind readonly }
diff --git a/llvm/test/CodeGen/AMDGPU/merge-image-load-gfx10.mir b/llvm/test/CodeGen/AMDGPU/merge-image-load-gfx10.mir
index 687c27d16ed3a..1d57e36c7046a 100644
--- a/llvm/test/CodeGen/AMDGPU/merge-image-load-gfx10.mir
+++ b/llvm/test/CodeGen/AMDGPU/merge-image-load-gfx10.mir
@@ -189,6 +189,24 @@ body:             |
 ...
 ---
 
+# GFX10-LABEL: name: image_load_dim_mismatch_not_merged
+# GFX10: %{{[0-9]+}}:vgpr_32 = IMAGE_LOAD_V1_V2_gfx10 %5, %3, 1, 1, -1, 0, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s32), addrspace 4)
+# GFX10: %{{[0-9]+}}:vreg_96 = IMAGE_LOAD_V3_V2_gfx10 %5, %3, 14, 4, -1, 0, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s96), align 16, addrspace 4)
+
+name: image_load_dim_mismatch_not_merged
+body:             |
+  bb.0.entry:
+    %0:sgpr_64 = COPY $sgpr0_sgpr1
+    %1:sreg_64_xexec = S_LOAD_DWORDX2_IMM %0, 36, 0
+    %2:sgpr_128 = COPY $sgpr96_sgpr97_sgpr98_sgpr99
+    %3:sgpr_256 = S_LOAD_DWORDX8_IMM %1, 208, 0
+    %4:vgpr_32 = COPY %2.sub3
+    %5:vreg_64 = BUFFER_LOAD_DWORDX2_OFFSET %2:sgpr_128, 0, 0, 0, 0, implicit $exec :: (dereferenceable invariant load (s128))
+    %6:vgpr_32 = IMAGE_LOAD_V1_V2_gfx10 %5:vreg_64, %3:sgpr_256, 1, 1, -1, 0, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s32), addrspace 4)
+    %7:vreg_96 = IMAGE_LOAD_V3_V2_gfx10 %5:vreg_64, %3:sgpr_256, 14, 4, -1, 0, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s96), align 16, addrspace 4)
+...
+---
+
 # GFX10-LABEL: name: image_load_not_merged_0
 # GFX10: %{{[0-9]+}}:vgpr_32 = IMAGE_LOAD_V1_V2_gfx10 %5, %3, 8, 1, -1, 0, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s32), addrspace 4)
 # GFX10: %{{[0-9]+}}:vreg_96 = IMAGE_LOAD_V3_V2_gfx10 %6, %3, 7, 1, -1, 0, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s96), align 16, addrspace 4)
diff --git a/llvm/test/CodeGen/AMDGPU/merge-image-load-gfx11.mir b/llvm/test/CodeGen/AMDGPU/merge-image-load-gfx11.mir
index 2edfdbef9974d..7343ab22bc0a6 100644
--- a/llvm/test/CodeGen/AMDGPU/merge-image-load-gfx11.mir
+++ b/llvm/test/CodeGen/AMDGPU/merge-image-load-gfx11.mir
@@ -189,6 +189,24 @@ body:             |
 ...
 ---
 
+# GFX11-LABEL: name: image_load_dim_mismatch_not_merged
+# GFX11: %{{[0-9]+}}:vgpr_32 = IMAGE_LOAD_V1_V2_gfx11 %5, %3, 1, 1, -1, 0, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s32), addrspace 4)
+# GFX11: %{{[0-9]+}}:vreg_96 = IMAGE_LOAD_V3_V2_gfx11 %5, %3, 14, 4, -1, 0, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s96), align 16, addrspace 4)
+
+name: image_load_dim_mismatch_not_merged
+body:             |
+  bb.0.entry:
+    %0:sgpr_64 = COPY $sgpr0_sgpr1
+    %1:sreg_64_xexec = S_LOAD_DWORDX2_IMM %0, 36, 0
+    %2:sgpr_128 = COPY $sgpr96_sgpr97_sgpr98_sgpr99
+    %3:sgpr_256 = S_LOAD_DWORDX8_IMM %1, 208, 0
+    %4:vgpr_32 = COPY %2.sub3
+    %5:vreg_64 = BUFFER_LOAD_DWORDX2_OFFSET %2:sgpr_128, 0, 0, 0, 0, implicit $exec :: (dereferenceable invariant load 16)
+    %6:vgpr_32 = IMAGE_LOAD_V1_V2_gfx11 %5:vreg_64, %3:sgpr_256, 1, 1, -1, 0, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load 4, addrspace 4)
+    %7:vreg_96 = IMAGE_LOAD_V3_V2_gfx11 %5:vreg_64, %3:sgpr_256, 14, 4, -1, 0, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load 12, align 16, addrspace 4)
+...
+---
+
 # GFX11-LABEL: name: image_load_not_merged_0
 # GFX11: %{{[0-9]+}}:vgpr_32 = IMAGE_LOAD_V1_V2_gfx11 %5, %3, 8, 1, -1, 0, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s32), addrspace 4)
 # GFX11: %{{[0-9]+}}:vreg_96 = IMAGE_LOAD_V3_V2_gfx11 %6, %3, 7, 1, -1, 0, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s96), align 16, addrspace 4)
diff --git a/llvm/test/CodeGen/AMDGPU/merge-image-load-gfx12.mir b/llvm/test/CodeGen/AMDGPU/merge-image-load-gfx12.mir
index a9d4033e72527..a5172e4f446de 100644
--- a/llvm/test/CodeGen/AMDGPU/merge-image-load-gfx12.mir
+++ b/llvm/test/CodeGen/AMDGPU/merge-image-load-gfx12.mir
@@ -212,6 +212,26 @@ body:             |
 ...
 ---
 
+# GFX12-LABEL: name: image_load_dim_mismatch_not_merged
+# GFX12: %{{[0-9]+}}:vgpr_32 = IMAGE_LOAD_V1_V2_gfx12 %6, %7, %3, 1, 1, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s32), addrspace 4)
+# GFX12: %{{[0-9]+}}:vreg_96 = IMAGE_LOAD_V3_V2_gfx12 %6, %7, %3, 14, 4, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s96), align 16, addrspace 4)
+
+name: image_load_dim_mismatch_not_merged
+body:             |
+  bb.0.entry:
+    %0:sgpr_64 = COPY $sgpr0_sgpr1
+    %1:sreg_64_xexec = S_LOAD_DWORDX2_IMM %0, 36, 0
+    %2:sgpr_128 = COPY $sgpr96_sgpr97_sgpr98_sgpr99
+    %3:sgpr_256 = S_LOAD_DWORDX8_IMM %1, 208, 0
+    %4:vgpr_32 = COPY %2.sub3
+    %5:vreg_64 = BUFFER_LOAD_DWORDX2_OFFSET %2:sgpr_128, 0, 0, 0, 0, implicit $exec :: (dereferenceable invariant load 16)
+    %6:vgpr_32 = COPY %5.sub0
+    %7:vgpr_32 = COPY %5.sub1
+    %8:vgpr_32 = IMAGE_LOAD_V1_V2_gfx12 %6:vgpr_32, %7:vgpr_32, %3:sgpr_256, 1, 1, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load 4, addrspace 4)
+    %9:vreg_96 = IMAGE_LOAD_V3_V2_gfx12 %6:vgpr_32, %7:vgpr_32, %3:sgpr_256, 14, 4, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load 12, align 16, addrspace 4)
+...
+---
+
 # GFX12-LABEL: name: image_load_not_merged_0
 # GFX12: %{{[0-9]+}}:vgpr_32 = IMAGE_LOAD_V1_V2_gfx12 %6, %7, %3, 8, 1, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s32), addrspace 4)
 # GFX12: %{{[0-9]+}}:vreg_96 = IMAGE_LOAD_V3_V2_gfx12 %9, %10, %3, 7, 1, 0, 0, 0, 0, 0, implicit $exec :: (dereferenceable load (s96), align 16, addrspace 4)

``````````

</details>


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


More information about the llvm-commits mailing list