Bug type: crash
Component: LoopAccessAnalysis
Bug location: llvm/lib/Analysis/LoopAccessAnalysis.cpp: AccessAnalysis::processMemAccesses
Issue title: LoopVectorizer `assert(AddressSpace == AS && with addrspacecast` is hit
Labels: release:backport
release:cherry-pick-failed
crash
llvm:analysis
Reported by: coldav

The following code produces an assert in  RuntimeCheckingPtrGroup::addPointer()


``` llvm
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-unknown-elf"

define void @__mux_host_0(ptr addrspace(1) %_arg_resultPtr, ptr addrspace(1) %add.ptr.i.sroa_idx.i.i.i.i) {
loopIR.i.preheader:
  %0 = alloca i8, i64 0, align 128
  %_arg_localAccessor = addrspacecast ptr %0 to ptr addrspace(3)
  %arrayidx.ascast.i.i.i.i.i.i = addrspacecast ptr %0 to ptr addrspace(4)
  br label %loopIR2.i

loopIR2.i:                                        ; preds = %loopIR2.i, %loopIR.i.preheader
  %1 = phi i64 [ 0, %loopIR.i.preheader ], [ %3, %loopIR2.i ]
  store i32 0, ptr addrspace(4) %arrayidx.ascast.i.i.i.i.i.i, align 4
  store float 0.000000e+00, ptr addrspace(1) %add.ptr.i.sroa_idx.i.i.i.i, align 4
  %2 = load i64, ptr addrspace(3) %_arg_localAccessor, align 4
  store i64 0, ptr addrspace(1) %_arg_resultPtr, align 4
  %3 = add i64 %1, 1
  br i1 false, label %loopIR2.i, label %exitIR.i

exitIR.i:                                         ; preds = %loopIR2.i
  ret void
}
```

This crashes with `opt --passes loop-vectorize /tmp/reduced.ll -S -o -` as follows:

```
./bin/opt --passes loop-vectorize /tmp/reduced.ll -S -o -
opt: /home/colin/llvm-project/llvm/lib/Analysis/LoopAccessAnalysis.cpp:423: bool llvm::RuntimeCheckingPtrGroup::addPointer(unsigned int, const llvm::SCEV*, const llvm::SCEV*, unsigned int, bool, llvm::ScalarEvolution&): Assertion `AddressSpace == AS && "all pointers in a checking group must be in the same address space"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: ./bin/opt --passes loop-vectorize /tmp/reduced.ll -S -o -
1.      Running pass "function(loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>)" on module "/tmp/reduced.ll"
2.      Running pass "loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>" on function "__mux_host_0"
 #0 0x00005a58a99c2ddc llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/colin/llvm-project/llvm/lib/Support/Unix/Signals.inc:798:22
 #1 0x00005a58a99c31fd PrintStackTraceSignalHandler(void*) /home/colin/llvm-project/llvm/lib/Support/Unix/Signals.inc:874:1
 #2 0x00005a58a99c0663 llvm::sys::RunSignalHandlers() /home/colin/llvm-project/llvm/lib/Support/Signals.cpp:105:20
 #3 0x00005a58a99c2674 SignalHandler(int) /home/colin/llvm-project/llvm/lib/Support/Unix/Signals.inc:415:1
 #4 0x00007c96b3242520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #5 0x00007c96b32969fc __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #6 0x00007c96b32969fc __pthread_kill_internal ./nptl/pthread_kill.c:78:10
 #7 0x00007c96b32969fc pthread_kill ./nptl/pthread_kill.c:89:10
 #8 0x00007c96b3242476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
 #9 0x00007c96b32287f3 abort ./stdlib/abort.c:81:7
#10 0x00007c96b322871b _nl_load_domain ./intl/loadmsgcat.c:1177:9
#11 0x00007c96b3239e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
#12 0x00005a58a8936a8b llvm::RuntimeCheckingPtrGroup::addPointer(unsigned int, llvm::SCEV const*, llvm::SCEV const*, unsigned int, bool, llvm::ScalarEvolution&) /home/colin/llvm-project/llvm/lib/Analysis/LoopAccessAnalysis.cpp:429:37
#13 0x00005a58a8936a20 llvm::RuntimeCheckingPtrGroup::addPointer(unsigned int, llvm::RuntimePointerChecking const&) /home/colin/llvm-project/llvm/lib/Analysis/LoopAccessAnalysis.cpp:413:20
#14 0x00005a58a8936f8d llvm::RuntimePointerChecking::groupChecks(llvm::EquivalenceClasses<llvm::PointerIntPair<llvm::Value*, 1u, bool, llvm::PointerLikeTypeTraits<llvm::Value*>, llvm::PointerIntPairInfo<llvm::Value*, 1u, llvm::PointerLikeTypeTraits<llvm::Value*>>>, std::less<llvm::PointerIntPair<llvm::Value*, 1u, bool, llvm::PointerLikeTypeTraits<llvm::Value*>, llvm::PointerIntPairInfo<llvm::Value*, 1u, llvm::PointerLikeTypeTraits<llvm::Value*>>>>>&, bool) /home/colin/llvm-project/llvm/lib/Analysis/LoopAccessAnalysis.cpp:554:11
#15 0x00005a58a893678d llvm::RuntimePointerChecking::generateChecks(llvm::EquivalenceClasses<llvm::PointerIntPair<llvm::Value*, 1u, bool, llvm::PointerLikeTypeTraits<llvm::Value*>, llvm::PointerIntPairInfo<llvm::Value*, 1u, llvm::PointerLikeTypeTraits<llvm::Value*>>>, std::less<llvm::PointerIntPair<llvm::Value*, 1u, bool, llvm::PointerLikeTypeTraits<llvm::Value*>, llvm::PointerIntPairInfo<llvm::Value*, 1u, llvm::PointerLikeTypeTraits<llvm::Value*>>>>>&, bool) /home/colin/llvm-project/llvm/lib/Analysis/LoopAccessAnalysis.cpp:389:26
#16 0x00005a58a893a016 (anonymous namespace)::AccessAnalysis::canCheckPtrAtRT(llvm::RuntimePointerChecking&, llvm::ScalarEvolution*, llvm::Loop*, llvm::DenseMap<llvm::Value*, llvm::SCEV const*, llvm::DenseMapInfo<llvm::Value*, void>, llvm::detail::DenseMapPair<llvm::Value*, llvm::SCEV const*>> const&, llvm::Value*&, bool) /home/colin/llvm-project/llvm/lib/Analysis/LoopAccessAnalysis.cpp:1243:3
#17 0x00005a58a8940750 llvm::LoopAccessInfo::analyzeLoop(llvm::AAResults*, llvm::LoopInfo const*, llvm::TargetLibraryInfo const*, llvm::DominatorTree*) /home/colin/llvm-project/llvm/lib/Analysis/LoopAccessAnalysis.cpp:2638:31
#18 0x00005a58a89422b3 llvm::LoopAccessInfo::LoopAccessInfo(llvm::Loop*, llvm::ScalarEvolution*, llvm::TargetTransformInfo const*, llvm::TargetLibraryInfo const*, llvm::AAResults*, llvm::DominatorTree*, llvm::LoopInfo*) /home/colin/llvm-project/llvm/lib/Analysis/LoopAccessAnalysis.cpp:3028:15
#19 0x00005a58a894ae40 std::_MakeUniq<llvm::LoopAccessInfo>::__single_object std::make_unique<llvm::LoopAccessInfo, llvm::Loop*, llvm::ScalarEvolution*, llvm::TargetTransformInfo*&, llvm::TargetLibraryInfo const*&, llvm::AAResults*, llvm::DominatorTree*, llvm::LoopInfo*>(llvm::Loop*&&, llvm::ScalarEvolution*&&, llvm::TargetTransformInfo*&, llvm::TargetLibraryInfo const*&, llvm::AAResults*&&, llvm::DominatorTree*&&, llvm::LoopInfo*&&) /usr/include/c++/11/bits/unique_ptr.h:962:30
#20 0x00005a58a8942883 llvm::LoopAccessInfoManager::getInfo(llvm::Loop&) /home/colin/llvm-project/llvm/lib/Analysis/LoopAccessAnalysis.cpp:3084:41
#21 0x00005a58a831900e llvm::LoopVectorizationLegality::canVectorizeMemory() /home/colin/llvm-project/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp:1208:7
#22 0x00005a58a831bc45 llvm::LoopVectorizationLegality::canVectorize(bool) /home/colin/llvm-project/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp:1840:7
#23 0x00005a58a800b8b5 llvm::LoopVectorizePass::processLoop(llvm::Loop*) /home/colin/llvm-project/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp:10349:7
#24 0x00005a58a800dde6 llvm::LoopVectorizePass::runImpl(llvm::Function&) /home/colin/llvm-project/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp:10779:27
#25 0x00005a58a800e133 llvm::LoopVectorizePass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /home/colin/llvm-project/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp:10816:39
#26 0x00005a58a65ef71b llvm::detail::PassModel<llvm::Function, llvm::LoopVectorizePass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /home/colin/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:92:3
#27 0x00005a58a96e6133 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /home/colin/llvm-project/llvm/include/llvm/IR/PassManagerImpl.h:85:18
#28 0x00005a58a4a21a65 llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /home/colin/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:92:3
#29 0x00005a58a96e513e llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/colin/llvm-project/llvm/lib/IR/PassManager.cpp:129:23
#30 0x00005a58a4a21995 llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/colin/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:92:3
#31 0x00005a58a96e5d5f llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/colin/llvm-project/llvm/include/llvm/IR/PassManagerImpl.h:85:18
#32 0x00005a58a476e632 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool) /home/colin/llvm-project/llvm/tools/opt/NewPMDriver.cpp:541:10
#33 0x00005a58a473c69f optMain /home/colin/llvm-project/llvm/tools/opt/optdriver.cpp:739:27
#34 0x00005a58a4739e81 main /home/colin/llvm-project/llvm/tools/opt/opt.cpp:25:64
#35 0x00007c96b3229d90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#36 0x00007c96b3229e40 call_init ./csu/../csu/libc-start.c:128:20
#37 0x00007c96b3229e40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#38 0x00005a58a4739d65 _start (./bin/opt+0xb93d65)
```

I've done some debugging and in `RuntimeCheckingPtrGroup::addPointer()` II can see the first two elements if RTCheck have the same dependency set but different address spaces.

I did try removing the dependency set being equal continuing the loop here https://github.com/llvm/llvm-project/blob/main/llvm/lib/Analysis/LoopAccessAnalysis.cpp#L1219

``` cpp
      // Only need to check pointers between two different dependency sets.
      if (RtCheck.Pointers[i].DependencySetId ==
          RtCheck.Pointers[j].DependencySetId)
       continue;
```
This does seem to fix the issue but I was wary on whether this was just another symptom rather than the proper fix.


Comments: author: coldav
body: Since nobody is looking at this, and it shows up as an assert for this, I'll try upstreaming my fix.
author: fhahn
body: @coldav sorry I missed this one. I think skipping the check you mentioned is probably not the right fix. I am looking into avoiding grouping pointers with different address spaces in the same dependency set.

I am not super familiar with address space semantics, I am not sure if it is valid to access the same object with pointers to different address spaces?
author: hvdijk
body: > I am not super familiar with address space semantics, I am not sure if it is valid to access the same object with pointers to different address spaces?

It depends on the address spaces. Address spaces may or may not overlap. If they do not overlap, then LLVM can assume that an object accessed from one address space and an object accessed from another do not alias. If the address spaces do overlap, however, that assumption is not valid. For the X86 target, address spaces 1-255 are reserved for user-defined code (https://llvm.org/docs/CodeGenerator.html#x86-address-spaces-supported) and interpreted for codegen purposes as aliases for address space zero, meaning all these address spaces overlap exactly.
author: fhahn
body: Put up https://github.com/llvm/llvm-project/pull/129087 to avoid adding pointers from different addrspaces in the same dependency set
author: coldav
body: Thanks @fhahn 
author: fhahn
body: Manual PR to pick https://github.com/llvm/llvm-project/pull/129317

Fix patch:
commit 275baedfde9dcd344bc4f11f552b046a69a4bf3f
Author: Florian Hahn <flo@fhahn.com>
Date:   Fri Feb 28 20:56:12 2025 +0000

    [LAA] Consider accessed addrspace when mapping underlying obj to access. (#129087)
    
    In some cases, it is possible for the same underlying object to be
    accessed via pointers to different address spaces. This could lead to
    pointers from different address spaces ending up in the same dependency
    set, which isn't allowed (and triggers an assertion).
    
    Update the mapping from underlying object -> last access to also include
    the accessing address space.
    
    Fixes https://github.com/llvm/llvm-project/issues/124759.
    
    PR: https://github.com/llvm/llvm-project/pull/129087

diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
index cf3bb6a8eae1..38ee82b77a94 100644
--- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
@@ -1362,8 +1362,10 @@ void AccessAnalysis::processMemAccesses() {
 
     bool SetHasWrite = false;
 
-    // Map of pointers to last access encountered.
-    typedef DenseMap<const Value*, MemAccessInfo> UnderlyingObjToAccessMap;
+    // Map of (pointer to underlying objects, accessed address space) to last
+    // access encountered.
+    typedef DenseMap<std::pair<const Value *, unsigned>, MemAccessInfo>
+        UnderlyingObjToAccessMap;
     UnderlyingObjToAccessMap ObjToLastAccess;
 
     // Set of access to check after all writes have been processed.
@@ -1443,8 +1445,10 @@ void AccessAnalysis::processMemAccesses() {
                     UnderlyingObj->getType()->getPointerAddressSpace()))
               continue;
 
-            auto [It, Inserted] =
-                ObjToLastAccess.try_emplace(UnderlyingObj, Access);
+            auto [It, Inserted] = ObjToLastAccess.try_emplace(
+                {UnderlyingObj,
+                 cast<PointerType>(Ptr->getType())->getAddressSpace()},
+                Access);
             if (!Inserted) {
               DepCands.unionSets(Access, It->second);
               It->second = Access;


Regression tests:
file: llvm/test/Analysis/LoopAccessAnalysis/underlying-object-different-address-spaces.ll
commands: opt -passes='print<access-info>' -disable-output %s 2>&1
tests: test_name: <module>
test_body: 
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"

; Test case for https://github.com/llvm/llvm-project/issues/124759. The same
; underlying object is access through pointers with different address spaces.
define void @same_underlying_object_different_address_spaces(ptr %dst1.as1, ptr %dst2.as1) {
;
entry:
  %alloc = alloca i8, i64 0, align 128
  %as3 = addrspacecast ptr %alloc to ptr addrspace(3)
  %as4 = addrspacecast ptr %alloc to ptr addrspace(4)
  br label %loop

loop:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  store i32 0, ptr addrspace(4) %as4, align 4
  store i32 0, ptr %dst1.as1, align 4
  %l = load i64, ptr addrspace(3) %as3, align 4
  store i64 %l, ptr %dst2.as1, align 4
  %iv.next = add i64 %iv, 1
  %c = icmp eq i64 %iv.next, 100
  br i1 %c, label %loop, label %exit

exit:
  ret void
}