[llvm] [AMDGPU] Reject image-load merges when either op has TFE/LWE (PR #210583)
Yudistira Putra via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 19 02:03:11 PDT 2026
https://github.com/Yudis-bit created https://github.com/llvm/llvm-project/pull/210583
## 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
>From 77dbb5d17d8e238f6e40a74fcea0e61dec1c077a Mon Sep 17 00:00:00 2001
From: Yudistira Putra <85178972+Yudis-bit at users.noreply.github.com>
Date: Sun, 19 Jul 2026 05:00:24 -0400
Subject: [PATCH] [AMDGPU] Reject image-load merges when either op has TFE/LWE
SILoadStoreOptimizer merges image loads directionally and inherits
non-dmask operands from the leading instruction only. A later TFE or
LWE image load could therefore be merged with an earlier ordinary load
and lose its status-return semantics.
Reject pairs when either instruction has TFE or LWE enabled, matching
the pass policy that these status results are outside the current merge
model. Add MIR coverage for ordinary-then-TFE and ordinary-then-LWE
orderings; existing cases already cover the reverse orderings and
status-free merges.
Fixes #187335
---
.../Target/AMDGPU/SILoadStoreOptimizer.cpp | 10 ++++--
llvm/test/CodeGen/AMDGPU/merge-image-load.mir | 35 +++++++++++++++++++
2 files changed, 43 insertions(+), 2 deletions(-)
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)
+...
+---
More information about the llvm-commits
mailing list