[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