[llvm] [AMDGPU] Reject image-load merges when either op has TFE/LWE (PR #210583)

via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 19 03:26:57 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: Yudistira Putra (Yudis-bit)

<details>
<summary>Changes</summary>

## Summary

Prevent `SILoadStoreOptimizer` from merging image instructions when either member of the pair uses TFE or LWE status-return semantics.

## Problem

The image-load merge is directional. Non-dmask operands for the merged instruction are inherited from the leading instruction. When an ordinary image load is followed by a compatible TFE/LWE image load, the pair can be merged without preserving the later instruction's status-result semantics.

This is the asymmetric ordering reported in #<!-- -->187335. The existing one-sided guard only inspected the leading instruction, so ordinary → TFE/LWE was admitted while TFE/LWE → ordinary was already rejected.

## Fix

Reject a merge when either instruction has TFE or LWE enabled. The current merge model sizes destinations from `popcount(dmask)` only and does not reconstruct status lanes, so exclusion matches the existing pass policy.

## Testing

- Added MIR regression coverage for ordinary → TFE (`image_load_tfe_second_not_merged`).
- Added MIR regression coverage for ordinary → LWE (`image_load_lwe_second_not_merged`).
- Verified reverse orderings remain unmerged (`image_load_not_merged_7/8`).
- Verified compatible status-free image loads still merge (`image_load_merged_v1v3` and existing positive cases).
- Ran `merge-image-load.mir` plus `merge-image-load-gfx{10,11,12}.mir` with assertions-enabled AMDGPU `llc` + FileCheck.

Fixes #<!-- -->187335

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


2 Files Affected:

- (modified) llvm/lib/Target/AMDGPU/SILoadStoreOptimizer.cpp (+8-2) 
- (modified) llvm/test/CodeGen/AMDGPU/merge-image-load.mir (+35) 


``````````diff
diff --git a/llvm/lib/Target/AMDGPU/SILoadStoreOptimizer.cpp b/llvm/lib/Target/AMDGPU/SILoadStoreOptimizer.cpp
index 3d82ef9249c44..cd63be51f6d48 100644
--- a/llvm/lib/Target/AMDGPU/SILoadStoreOptimizer.cpp
+++ b/llvm/lib/Target/AMDGPU/SILoadStoreOptimizer.cpp
@@ -1011,11 +1011,17 @@ bool SILoadStoreOptimizer::dmasksCanBeCombined(const CombineInfo &CI,
                                                const CombineInfo &Paired) {
   assert(CI.InstClass == MIMG);
 
-  // Ignore instructions with tfe/lwe set.
+  // Ignore instructions with tfe/lwe set on either member of the pair.
+  // mergeImagePair copies non-dmask operands only from CI.I, so a later
+  // TFE/LWE instruction would otherwise lose its status-return semantics.
   const auto *TFEOp = TII.getNamedOperand(*CI.I, AMDGPU::OpName::tfe);
   const auto *LWEOp = TII.getNamedOperand(*CI.I, AMDGPU::OpName::lwe);
+  const auto *PairedTFEOp = TII.getNamedOperand(*Paired.I, AMDGPU::OpName::tfe);
+  const auto *PairedLWEOp = TII.getNamedOperand(*Paired.I, AMDGPU::OpName::lwe);
 
-  if ((TFEOp && TFEOp->getImm()) || (LWEOp && LWEOp->getImm()))
+  if ((TFEOp && TFEOp->getImm()) || (LWEOp && LWEOp->getImm()) ||
+      (PairedTFEOp && PairedTFEOp->getImm()) ||
+      (PairedLWEOp && PairedLWEOp->getImm()))
     return false;
 
   // Check other optional immediate operands for equality.
diff --git a/llvm/test/CodeGen/AMDGPU/merge-image-load.mir b/llvm/test/CodeGen/AMDGPU/merge-image-load.mir
index 2d3f9b0f482a7..dfebc1cc4a608 100644
--- a/llvm/test/CodeGen/AMDGPU/merge-image-load.mir
+++ b/llvm/test/CodeGen/AMDGPU/merge-image-load.mir
@@ -485,3 +485,38 @@ body:             |
 ...
 ---
 
+# GFX9-LABEL: name: image_load_tfe_second_not_merged
+# GFX9: %{{[0-9]+}}:vgpr_32 = IMAGE_LOAD_V1_V4 %5, %3, 8, 0, 0, 0, 0, 0, -1, 0, implicit $exec :: (dereferenceable load (s32), addrspace 4)
+# GFX9: %{{[0-9]+}}:vreg_128 = IMAGE_LOAD_V4_V4 %5, %3, 7, 0, 0, 0, 1, 0, -1, 0, implicit $exec :: (dereferenceable load (s96), align 16, addrspace 4)
+
+name: image_load_tfe_second_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_128 = BUFFER_LOAD_DWORDX4_OFFSET %2, 0, 0, 0, 0, implicit $exec :: (dereferenceable invariant load (s128))
+    %6:vgpr_32 = IMAGE_LOAD_V1_V4 %5, %3, 8, 0, 0, 0, 0, 0, -1, 0, implicit $exec :: (dereferenceable load (s32), addrspace 4)
+    %7:vreg_128 = IMAGE_LOAD_V4_V4 %5, %3, 7, 0, 0, 0, 1, 0, -1, 0, implicit $exec :: (dereferenceable load (s96), align 16, addrspace 4)
+...
+---
+
+# GFX9-LABEL: name: image_load_lwe_second_not_merged
+# GFX9: %{{[0-9]+}}:vgpr_32 = IMAGE_LOAD_V1_V4 %5, %3, 8, 0, 0, 0, 0, 0, -1, 0, implicit $exec :: (dereferenceable load (s32), addrspace 4)
+# GFX9: %{{[0-9]+}}:vreg_128 = IMAGE_LOAD_V4_V4 %5, %3, 7, 0, 0, 0, 0, 1, -1, 0, implicit $exec :: (dereferenceable load (s96), align 16, addrspace 4)
+
+name: image_load_lwe_second_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_128 = BUFFER_LOAD_DWORDX4_OFFSET %2, 0, 0, 0, 0, implicit $exec :: (dereferenceable invariant load (s128))
+    %6:vgpr_32 = IMAGE_LOAD_V1_V4 %5, %3, 8, 0, 0, 0, 0, 0, -1, 0, implicit $exec :: (dereferenceable load (s32), addrspace 4)
+    %7:vreg_128 = IMAGE_LOAD_V4_V4 %5, %3, 7, 0, 0, 0, 0, 1, -1, 0, implicit $exec :: (dereferenceable load (s96), align 16, addrspace 4)
+...
+---

``````````

</details>


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


More information about the llvm-commits mailing list