[llvm] [AggressiveInstCombine] Don't merge part stores across address spaces (PR #213677)

Pankaj Dwivedi via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 3 08:11:53 PDT 2026


https://github.com/PankajDwivedi-25 updated https://github.com/llvm/llvm-project/pull/213677

>From b237396df48edf3245c14d53f270d2cd0e150c64 Mon Sep 17 00:00:00 2001
From: padivedi <pankajkumar.divedi at amd.com>
Date: Mon, 3 Aug 2026 19:16:47 +0530
Subject: [PATCH] [AggressiveInstCombine] Don't merge part stores across
 address spaces

---
 .../AggressiveInstCombine.cpp                 |   6 +-
 .../AMDGPU/store-merge-addrspace.ll           | 102 ++++++++++++++++++
 2 files changed, 107 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/Transforms/AggressiveInstCombine/AMDGPU/store-merge-addrspace.ll

diff --git a/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp b/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp
index cef41c971aa18..8395c63b7338c 100644
--- a/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp
+++ b/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp
@@ -1506,7 +1506,11 @@ struct PartStore {
   StoreInst *Store;
 
   bool isCompatibleWith(const PartStore &Other) const {
-    return PtrBase == Other.PtrBase && Val == Other.Val;
+    // Offset stripping looks through addrspacecasts, so an equal PtrBase does
+    // not imply an equal address space, and thus not an equal PtrOffset width.
+    return PtrBase == Other.PtrBase && Val == Other.Val &&
+           Store->getPointerAddressSpace() ==
+               Other.Store->getPointerAddressSpace();
   }
 
   bool operator<(const PartStore &Other) const {
diff --git a/llvm/test/Transforms/AggressiveInstCombine/AMDGPU/store-merge-addrspace.ll b/llvm/test/Transforms/AggressiveInstCombine/AMDGPU/store-merge-addrspace.ll
new file mode 100644
index 0000000000000..5503e0808b78f
--- /dev/null
+++ b/llvm/test/Transforms/AggressiveInstCombine/AMDGPU/store-merge-addrspace.ll
@@ -0,0 +1,102 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -mtriple=amdgpu9.42-amd-amdhsa -passes=aggressive-instcombine %s -S -o - | FileCheck %s
+
+; Parts stored through pointers in different address spaces must not be merged,
+; even though offset stripping reaches a common base through the addrspacecast.
+; The private address space has a 32 bit index, the flat one a 64 bit index, so
+; comparing the two part offsets used to assert.
+define void @mismatched_index_width(i32 %x, ptr addrspace(5) %p) {
+; CHECK-LABEL: define void @mismatched_index_width(
+; CHECK-SAME: i32 [[X:%.*]], ptr addrspace(5) [[P:%.*]]) {
+; CHECK-NEXT:    [[LO:%.*]] = trunc i32 [[X]] to i16
+; CHECK-NEXT:    store i16 [[LO]], ptr addrspace(5) [[P]], align 4
+; CHECK-NEXT:    [[F:%.*]] = addrspacecast ptr addrspace(5) [[P]] to ptr
+; CHECK-NEXT:    [[F_2:%.*]] = getelementptr i8, ptr [[F]], i64 2
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[X]], 16
+; CHECK-NEXT:    [[HI:%.*]] = trunc i32 [[SHR]] to i16
+; CHECK-NEXT:    store i16 [[HI]], ptr [[F_2]], align 2
+; CHECK-NEXT:    ret void
+;
+  %lo = trunc i32 %x to i16
+  store i16 %lo, ptr addrspace(5) %p, align 4
+  %f = addrspacecast ptr addrspace(5) %p to ptr
+  %f.2 = getelementptr i8, ptr %f, i64 2
+  %shr = lshr i32 %x, 16
+  %hi = trunc i32 %shr to i16
+  store i16 %hi, ptr %f.2, align 2
+  ret void
+}
+
+define void @mismatched_index_width_overlapping(i32 %x) {
+; CHECK-LABEL: define void @mismatched_index_width_overlapping(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[LO16:%.*]] = trunc i32 [[X]] to i16
+; CHECK-NEXT:    store i16 [[LO16]], ptr addrspace(5) null, align 8
+; CHECK-NEXT:    [[LO8:%.*]] = trunc i32 [[X]] to i8
+; CHECK-NEXT:    store i8 [[LO8]], ptr addrspacecast (ptr addrspace(5) null to ptr), align 1
+; CHECK-NEXT:    ret void
+;
+  %lo16 = trunc i32 %x to i16
+  store i16 %lo16, ptr addrspace(5) null, align 8
+  %lo8 = trunc i32 %x to i8
+  store i8 %lo8, ptr addrspacecast (ptr addrspace(5) null to ptr), align 1
+  ret void
+}
+
+; The global and flat address spaces share an index width, so the offsets are
+; comparable, but merging would still move the access into another address space.
+define void @cross_addrspace_same_index_width(i32 %x, ptr addrspace(1) %p) {
+; CHECK-LABEL: define void @cross_addrspace_same_index_width(
+; CHECK-SAME: i32 [[X:%.*]], ptr addrspace(1) [[P:%.*]]) {
+; CHECK-NEXT:    [[LO:%.*]] = trunc i32 [[X]] to i16
+; CHECK-NEXT:    store i16 [[LO]], ptr addrspace(1) [[P]], align 4
+; CHECK-NEXT:    [[F:%.*]] = addrspacecast ptr addrspace(1) [[P]] to ptr
+; CHECK-NEXT:    [[F_2:%.*]] = getelementptr i8, ptr [[F]], i64 2
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[X]], 16
+; CHECK-NEXT:    [[HI:%.*]] = trunc i32 [[SHR]] to i16
+; CHECK-NEXT:    store i16 [[HI]], ptr [[F_2]], align 2
+; CHECK-NEXT:    ret void
+;
+  %lo = trunc i32 %x to i16
+  store i16 %lo, ptr addrspace(1) %p, align 4
+  %f = addrspacecast ptr addrspace(1) %p to ptr
+  %f.2 = getelementptr i8, ptr %f, i64 2
+  %shr = lshr i32 %x, 16
+  %hi = trunc i32 %shr to i16
+  store i16 %hi, ptr %f.2, align 2
+  ret void
+}
+
+define void @same_addrspace_private(i32 %x, ptr addrspace(5) %p) {
+; CHECK-LABEL: define void @same_addrspace_private(
+; CHECK-SAME: i32 [[X:%.*]], ptr addrspace(5) [[P:%.*]]) {
+; CHECK-NEXT:    store i32 [[X]], ptr addrspace(5) [[P]], align 4
+; CHECK-NEXT:    ret void
+;
+  %lo = trunc i32 %x to i16
+  store i16 %lo, ptr addrspace(5) %p, align 4
+  %p.2 = getelementptr i8, ptr addrspace(5) %p, i32 2
+  %shr = lshr i32 %x, 16
+  %hi = trunc i32 %shr to i16
+  store i16 %hi, ptr addrspace(5) %p.2, align 2
+  ret void
+}
+
+; Both parts are stored in the flat address space, so looking through the
+; addrspacecast to a common base is still fine.
+define void @same_addrspace_through_addrspacecast(i32 %x, ptr addrspace(5) %p) {
+; CHECK-LABEL: define void @same_addrspace_through_addrspacecast(
+; CHECK-SAME: i32 [[X:%.*]], ptr addrspace(5) [[P:%.*]]) {
+; CHECK-NEXT:    [[F:%.*]] = addrspacecast ptr addrspace(5) [[P]] to ptr
+; CHECK-NEXT:    store i32 [[X]], ptr [[F]], align 4
+; CHECK-NEXT:    ret void
+;
+  %f = addrspacecast ptr addrspace(5) %p to ptr
+  %lo = trunc i32 %x to i16
+  store i16 %lo, ptr %f, align 4
+  %f.2 = getelementptr i8, ptr %f, i64 2
+  %shr = lshr i32 %x, 16
+  %hi = trunc i32 %shr to i16
+  store i16 %hi, ptr %f.2, align 2
+  ret void
+}



More information about the llvm-commits mailing list