[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