[llvm-branch-commits] [llvm] [Attributor] AAAddressSpace should use the AA for recursive lookups (PR #207866)

Johannes Doerfert via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Jul 7 11:02:53 PDT 2026


https://github.com/jdoerfert updated https://github.com/llvm/llvm-project/pull/207866

>From f3f427f9f707a07a46d2901d2cb61baa89e30d12 Mon Sep 17 00:00:00 2001
From: Johannes Doerfert <jdoerfert.llvm at gmail.com>
Date: Mon, 6 Jul 2026 15:03:18 -0700
Subject: [PATCH] [Attributor] AAAddressSpace should use the AA for recursive
 lookups

If we stripped some values, we can use the AAAddressSpace again for
an optimistic AS value. If we didn't strip any value, we can still use
the TLI.
---
 .../Transforms/IPO/AttributorAttributes.cpp   |  14 +-
 .../Attributor/AddrSpaceIndirect.ll           | 171 ++++++++++++++++++
 2 files changed, 181 insertions(+), 4 deletions(-)
 create mode 100644 llvm/test/Transforms/Attributor/AddrSpaceIndirect.ll

diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 6c780226c3fdf..f76e2dcc77b16 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -13100,7 +13100,6 @@ struct AAAddressSpaceImpl : public AAAddressSpace {
   void initialize(Attributor &A) override {
     assert(getAssociatedType()->isPtrOrPtrVectorTy() &&
            "Associated value is not a pointer");
-
     if (!A.getInfoCache().getFlatAddressSpace().has_value()) {
       indicatePessimisticFixpoint();
       return;
@@ -13118,6 +13117,7 @@ struct AAAddressSpaceImpl : public AAAddressSpace {
   ChangeStatus updateImpl(Attributor &A) override {
     uint32_t OldAddressSpace = AssumedAddressSpace;
     unsigned FlatAS = A.getInfoCache().getFlatAddressSpace().value();
+    Value &AssociatedValue = getAssociatedValue();
 
     auto CheckAddressSpace = [&](Value &Obj) {
       // Ignore undef.
@@ -13125,9 +13125,15 @@ struct AAAddressSpaceImpl : public AAAddressSpace {
         return true;
 
       // If the object already has a non-flat address space, we simply take it.
-      unsigned ObjAS = Obj.getType()->getPointerAddressSpace();
-      if (ObjAS != FlatAS)
-        return takeAddressSpace(ObjAS);
+      if (&Obj != &AssociatedValue) {
+        auto *ObjAA = A.getAAFor<AAAddressSpace>(*this, IRPosition::value(Obj),
+                                                 DepClassTy::OPTIONAL);
+        unsigned ObjAS = Obj.getType()->getPointerAddressSpace();
+        if (ObjAA && ObjAA->isValidState())
+          ObjAS = ObjAA->getAddressSpace();
+        if (ObjAS != FlatAS)
+          return takeAddressSpace(ObjAS);
+      }
 
       // At this point, we know Obj is in the flat address space. For a final
       // attempt, we want to use getAssumedAddrSpace, but first we must get the
diff --git a/llvm/test/Transforms/Attributor/AddrSpaceIndirect.ll b/llvm/test/Transforms/Attributor/AddrSpaceIndirect.ll
new file mode 100644
index 0000000000000..59ddfd22bad28
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/AddrSpaceIndirect.ll
@@ -0,0 +1,171 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals --prefix-filecheck-ir-name true
+; RUN: opt -mtriple=nvptx-unknown-unknown -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefix=CHECK
+
+ at _ZZ27mcasi_digit_counters_kernelE14shared_storage = internal addrspace(3) global [256 x i32] undef, align 4
+
+; Function Attrs: convergent mustprogress noinline norecurse nounwind
+;.
+; CHECK: @_ZZ27mcasi_digit_counters_kernelE14shared_storage = internal addrspace(3) global [256 x i32] undef, align 4
+;.
+define ptx_kernel void @mcasi_digit_counters_kernel(ptr nofree noundef writeonly captures(none) %arg, ptr nofree noundef readonly captures(none) %arg1, i32 noundef %arg2) #0 {
+; CHECK: Function Attrs: convergent mustprogress noinline norecurse nounwind
+; CHECK-LABEL: define {{[^@]+}}@mcasi_digit_counters_kernel
+; CHECK-SAME: (ptr nofree noundef writeonly captures(none) [[ARG:%.*]], ptr nofree noundef readonly captures(none) [[ARG1:%.*]], i32 noundef [[ARG2:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    [[I:%.*]] = alloca [4 x ptr], align 8
+; CHECK-NEXT:    [[I3:%.*]] = tail call noundef i32 @llvm.nvvm.read.ptx.sreg.tid.x() #[[ATTR4:[0-9]+]]
+; CHECK-NEXT:    [[I4:%.*]] = icmp samesign ult i32 [[I3]], 256
+; CHECK-NEXT:    br i1 [[I4]], label [[BB5:%.*]], label [[BB10:%.*]]
+; CHECK:       bb5:
+; CHECK-NEXT:    [[I6:%.*]] = zext nneg i32 [[I3]] to i64
+; CHECK-NEXT:    [[I7:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[ARG1]], i64 [[I6]]
+; CHECK-NEXT:    [[TMP0:%.*]] = addrspacecast ptr [[I7]] to ptr addrspace(1)
+; CHECK-NEXT:    [[I8:%.*]] = load i32, ptr addrspace(1) [[TMP0]], align 4
+; CHECK-NEXT:    [[I9:%.*]] = getelementptr inbounds nuw [4 x i8], ptr addrspacecast (ptr addrspace(3) @_ZZ27mcasi_digit_counters_kernelE14shared_storage to ptr), i64 [[I6]]
+; CHECK-NEXT:    [[TMP1:%.*]] = addrspacecast ptr [[I9]] to ptr addrspace(3)
+; CHECK-NEXT:    store i32 [[I8]], ptr addrspace(3) [[TMP1]], align 4
+; CHECK-NEXT:    br label [[BB10]]
+; CHECK:       bb10:
+; CHECK-NEXT:    tail call void @llvm.nvvm.barrier.cta.sync.aligned.all(i32 noundef 0)
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr noalias nofree noundef nonnull align 8 captures(none) dereferenceable(28) [[I]]) #[[ATTR5:[0-9]+]]
+; CHECK-NEXT:    [[I11:%.*]] = shl nuw nsw i32 [[I3]], 2
+; CHECK-NEXT:    [[I12:%.*]] = and i32 [[I11]], 124
+; CHECK-NEXT:    [[I13:%.*]] = zext nneg i32 [[I12]] to i64
+; CHECK-NEXT:    [[I14:%.*]] = getelementptr inbounds nuw [4 x i8], ptr addrspacecast (ptr addrspace(3) @_ZZ27mcasi_digit_counters_kernelE14shared_storage to ptr), i64 [[I13]]
+; CHECK-NEXT:    [[TMP2:%.*]] = addrspacecast ptr [[I]] to ptr addrspace(5)
+; CHECK-NEXT:    store ptr [[I14]], ptr addrspace(5) [[TMP2]], align 8
+; CHECK-NEXT:    [[I15:%.*]] = getelementptr inbounds nuw i8, ptr [[I14]], i64 4
+; CHECK-NEXT:    [[I16:%.*]] = getelementptr inbounds nuw i8, ptr [[I]], i64 8
+; CHECK-NEXT:    [[TMP3:%.*]] = addrspacecast ptr [[I16]] to ptr addrspace(5)
+; CHECK-NEXT:    store ptr [[I15]], ptr addrspace(5) [[TMP3]], align 8
+; CHECK-NEXT:    [[I17:%.*]] = getelementptr inbounds nuw i8, ptr [[I14]], i64 8
+; CHECK-NEXT:    [[I18:%.*]] = getelementptr inbounds nuw i8, ptr [[I]], i64 16
+; CHECK-NEXT:    [[TMP4:%.*]] = addrspacecast ptr [[I18]] to ptr addrspace(5)
+; CHECK-NEXT:    store ptr [[I17]], ptr addrspace(5) [[TMP4]], align 8
+; CHECK-NEXT:    [[I19:%.*]] = getelementptr inbounds nuw i8, ptr [[I14]], i64 12
+; CHECK-NEXT:    [[I20:%.*]] = getelementptr inbounds nuw i8, ptr [[I]], i64 24
+; CHECK-NEXT:    [[TMP5:%.*]] = addrspacecast ptr [[I20]] to ptr addrspace(5)
+; CHECK-NEXT:    store ptr [[I19]], ptr addrspace(5) [[TMP5]], align 8
+; CHECK-NEXT:    [[I21:%.*]] = and i32 [[ARG2]], 3
+; CHECK-NEXT:    [[I22:%.*]] = zext nneg i32 [[I21]] to i64
+; CHECK-NEXT:    [[I23:%.*]] = getelementptr inbounds nuw [8 x i8], ptr [[I]], i64 [[I22]]
+; CHECK-NEXT:    [[TMP6:%.*]] = addrspacecast ptr [[I23]] to ptr addrspace(5)
+; CHECK-NEXT:    [[I24:%.*]] = load ptr, ptr addrspace(5) [[TMP6]], align 8
+; CHECK-NEXT:    [[TMP7:%.*]] = addrspacecast ptr [[I24]] to ptr addrspace(3)
+; CHECK-NEXT:    [[I25:%.*]] = load i32, ptr addrspace(3) [[TMP7]], align 4, !invariant.load [[META15:![0-9]+]]
+; CHECK-NEXT:    [[I26:%.*]] = add i32 [[I25]], [[I21]]
+; CHECK-NEXT:    [[I27:%.*]] = zext nneg i32 [[I3]] to i64
+; CHECK-NEXT:    [[I28:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[ARG]], i64 [[I27]]
+; CHECK-NEXT:    [[TMP8:%.*]] = addrspacecast ptr [[I28]] to ptr addrspace(1)
+; CHECK-NEXT:    store i32 [[I26]], ptr addrspace(1) [[TMP8]], align 4
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr noalias nofree noundef nonnull align 8 captures(none) dereferenceable(28) [[I]]) #[[ATTR5]]
+; CHECK-NEXT:    ret void
+;
+bb:
+  %i = alloca [4 x ptr], align 8
+  %i3 = tail call noundef i32 @llvm.nvvm.read.ptx.sreg.tid.x()
+  %i4 = icmp samesign ult i32 %i3, 256
+  br i1 %i4, label %bb5, label %bb10
+
+bb5:                                              ; preds = %bb
+  %i6 = zext nneg i32 %i3 to i64
+  %i7 = getelementptr inbounds nuw [4 x i8], ptr %arg1, i64 %i6
+  %i8 = load i32, ptr %i7, align 4
+  %i9 = getelementptr inbounds nuw [4 x i8], ptr addrspacecast (ptr addrspace(3) @_ZZ27mcasi_digit_counters_kernelE14shared_storage to ptr), i64 %i6
+  store i32 %i8, ptr %i9, align 4
+  br label %bb10
+
+bb10:                                             ; preds = %bb5, %bb
+  tail call void @llvm.nvvm.barrier.cta.sync.aligned.all(i32 0)
+  call void @llvm.lifetime.start.p0(ptr nonnull %i) #4
+  %i11 = shl nuw nsw i32 %i3, 2
+  %i12 = and i32 %i11, 124
+  %i13 = zext nneg i32 %i12 to i64
+  %i14 = getelementptr inbounds nuw [4 x i8], ptr addrspacecast (ptr addrspace(3) @_ZZ27mcasi_digit_counters_kernelE14shared_storage to ptr), i64 %i13
+  store ptr %i14, ptr %i, align 8
+  %i15 = getelementptr inbounds nuw i8, ptr %i14, i64 4
+  %i16 = getelementptr inbounds nuw i8, ptr %i, i64 8
+  store ptr %i15, ptr %i16, align 8
+  %i17 = getelementptr inbounds nuw i8, ptr %i14, i64 8
+  %i18 = getelementptr inbounds nuw i8, ptr %i, i64 16
+  store ptr %i17, ptr %i18, align 8
+  %i19 = getelementptr inbounds nuw i8, ptr %i14, i64 12
+  %i20 = getelementptr inbounds nuw i8, ptr %i, i64 24
+  store ptr %i19, ptr %i20, align 8
+  %i21 = and i32 %arg2, 3
+  %i22 = zext nneg i32 %i21 to i64
+  %i23 = getelementptr inbounds nuw [8 x i8], ptr %i, i64 %i22
+  %i24 = load ptr, ptr %i23, align 8
+  %i25 = load i32, ptr %i24, align 4
+  %i26 = add i32 %i25, %i21
+  %i27 = zext nneg i32 %i3 to i64
+  %i28 = getelementptr inbounds nuw [4 x i8], ptr %arg, i64 %i27
+  store i32 %i26, ptr %i28, align 4
+  call void @llvm.lifetime.end.p0(ptr nonnull %i) #4
+  ret void
+}
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+declare void @llvm.lifetime.start.p0(ptr captures(none)) #1
+
+; Function Attrs: convergent nocallback nounwind
+declare void @llvm.nvvm.barrier.cta.sync.aligned.all(i32) #2
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+declare void @llvm.lifetime.end.p0(ptr captures(none)) #1
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare noundef range(i32 0, 1024) i32 @llvm.nvvm.read.ptx.sreg.tid.x() #3
+
+attributes #0 = { convergent mustprogress noinline norecurse nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="sm_120a" "target-features"="+ptx91,+sm_120a" "uniform-work-group-size" }
+attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+attributes #2 = { convergent nocallback nounwind }
+attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+attributes #4 = { nounwind }
+
+!llvm.module.flags = !{!0, !1, !2, !3, !4}
+!llvm.dbg.cu = !{!5}
+!llvm.ident = !{!7, !8}
+!llvm.errno.tbaa = !{!9}
+!nvvmir.version = !{!14}
+
+!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 13, i32 1]}
+!1 = !{i32 7, !"Dwarf Version", i32 2}
+!2 = !{i32 2, !"Debug Info Version", i32 3}
+!3 = !{i32 4, !"nvvm-reflect-ftz", i32 0}
+!4 = !{i32 7, !"frame-pointer", i32 2}
+!5 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !6, producer: "clang version 23.0.0git (https://github.com/llvm/llvm-project.git 774f8a0b868cf526ffbd8af0a7e83c6b70d1b4fb)", isOptimized: true, runtimeVersion: 0, emissionKind: DebugDirectivesOnly, splitDebugInlining: false, nameTableKind: None)
+!6 = !DIFile(filename: "/app/example.cu", directory: "/app")
+!7 = !{!"clang version 23.0.0git (https://github.com/llvm/llvm-project.git 774f8a0b868cf526ffbd8af0a7e83c6b70d1b4fb)"}
+!8 = !{!"clang version 3.8.0 (tags/RELEASE_380/final)"}
+!9 = !{!10, !11, i64 0}
+!10 = !{!"__libc_errno", !11, i64 0}
+!11 = !{!"int", !12, i64 0}
+!12 = !{!"omnipotent char", !13, i64 0}
+!13 = !{!"Simple C++ TBAA"}
+!14 = !{i32 2, i32 0}
+;.
+; CHECK: attributes #[[ATTR0]] = { convergent mustprogress noinline norecurse nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="sm_120a" "target-features"="+ptx91,+sm_120a" "uniform-work-group-size" }
+; CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+; CHECK: attributes #[[ATTR2:[0-9]+]] = { convergent nocallback nounwind }
+; CHECK: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+; CHECK: attributes #[[ATTR4]] = { willreturn }
+; CHECK: attributes #[[ATTR5]] = { nounwind }
+;.
+; CHECK: [[META0:![0-9]+]] = !{i32 2, !"SDK Version", [2 x i32] [i32 13, i32 1]}
+; CHECK: [[META1:![0-9]+]] = !{i32 7, !"Dwarf Version", i32 2}
+; CHECK: [[META2:![0-9]+]] = !{i32 2, !"Debug Info Version", i32 3}
+; CHECK: [[META3:![0-9]+]] = !{i32 4, !"nvvm-reflect-ftz", i32 0}
+; CHECK: [[META4:![0-9]+]] = !{i32 7, !"frame-pointer", i32 2}
+; CHECK: [[META5:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: [[META6:![0-9]+]], producer: "{{.*}}clang version {{.*}}", isOptimized: true, runtimeVersion: 0, emissionKind: DebugDirectivesOnly, splitDebugInlining: false, nameTableKind: None)
+; CHECK: [[META6]] = !DIFile(filename: "{{.*}}example.cu", directory: {{.*}})
+; CHECK: [[META7:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"}
+; CHECK: [[META8:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"}
+; CHECK: [[META9:![0-9]+]] = !{[[META10:![0-9]+]], [[META11:![0-9]+]], i64 0}
+; CHECK: [[META10]] = !{!"__libc_errno", [[META11]], i64 0}
+; CHECK: [[META11]] = !{!"int", [[META12:![0-9]+]], i64 0}
+; CHECK: [[META12]] = !{!"omnipotent char", [[META13:![0-9]+]], i64 0}
+; CHECK: [[META13]] = !{!"Simple C++ TBAA"}
+; CHECK: [[META14:![0-9]+]] = !{i32 2, i32 0}
+; CHECK: [[META15]] = !{}
+;.



More information about the llvm-branch-commits mailing list