[llvm] [SPIRV] Fix crash when two functions share an alias scope (PR #225017)

Nikhita Hegde via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 20 23:57:59 PDT 2026


https://github.com/Nikhita-P-Hegde created https://github.com/llvm/llvm-project/pull/225017

When two functions referenced the same alias scope metadata, they were incorrectly sharing a virtual register. This caused either a crash or broken SPIR-V output with undefined references. The fix ensures each function creates its own virtual register for alias scope metadata

>From 8a9779900e1d0d9bb2567ed0bcb2388fd249baec Mon Sep 17 00:00:00 2001
From: nikhitah <nikhita.hegde at intel.com>
Date: Thu, 17 Sep 2026 07:57:27 +0000
Subject: [PATCH] [SPIRV] Fix crash when two functions share an alias scope

AliasInstMDMap caches the MachineInstr created for an alias scope MDNode,
but those instructions define virtual registers that are only valid in the
function that created them. If two functions reference the same alias scope
metadata, the second reuses the first function's register and we either
index MachineRegisterInfo out of bounds or emit SPIR-V referencing an
undefined id.
Key the map on {MachineFunction, MDNode} so each function gets its own
declarations
---
 llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 16 ++++---
 llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h   |  7 ++-
 .../alias-scope-shared-across-functions.ll    | 46 +++++++++++++++++++
 3 files changed, 61 insertions(+), 8 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_memory_access_aliasing/alias-scope-shared-across-functions.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 021bc1806d284..a0eab4a541550 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -2265,7 +2265,9 @@ MachineInstr *SPIRVGlobalRegistry::getOrAddMemAliasingINTELInst(
     MachineIRBuilder &MIRBuilder, const MDNode *AliasingListMD) {
   if (AliasingListMD->getNumOperands() == 0)
     return nullptr;
-  if (auto L = AliasInstMDMap.find(AliasingListMD); L != AliasInstMDMap.end())
+  const MachineFunction *MF = &MIRBuilder.getMF();
+  if (auto L = AliasInstMDMap.find({MF, AliasingListMD});
+      L != AliasInstMDMap.end())
     return L->second;
 
   SmallVector<MachineInstr *> ScopeList;
@@ -2278,7 +2280,7 @@ MachineInstr *SPIRVGlobalRegistry::getOrAddMemAliasingINTELInst(
       if (!DomainMD)
         return nullptr;
       auto *Domain = [&] {
-        auto D = AliasInstMDMap.find(DomainMD);
+        auto D = AliasInstMDMap.find({MF, DomainMD});
         if (D != AliasInstMDMap.end())
           return D->second;
         const Register Ret = MRI->createVirtualRegister(&SPIRV::IDRegClass);
@@ -2286,9 +2288,10 @@ MachineInstr *SPIRVGlobalRegistry::getOrAddMemAliasingINTELInst(
             MIRBuilder.buildInstr(SPIRV::OpAliasDomainDeclINTEL).addDef(Ret);
         return MIB.getInstr();
       }();
-      AliasInstMDMap.insert(std::make_pair(DomainMD, Domain));
+      AliasInstMDMap.insert(
+          std::make_pair(std::make_pair(MF, DomainMD), Domain));
       auto *Scope = [&] {
-        auto S = AliasInstMDMap.find(ScopeMD);
+        auto S = AliasInstMDMap.find({MF, ScopeMD});
         if (S != AliasInstMDMap.end())
           return S->second;
         const Register Ret = MRI->createVirtualRegister(&SPIRV::IDRegClass);
@@ -2297,7 +2300,7 @@ MachineInstr *SPIRVGlobalRegistry::getOrAddMemAliasingINTELInst(
                        .addUse(Domain->getOperand(0).getReg());
         return MIB.getInstr();
       }();
-      AliasInstMDMap.insert(std::make_pair(ScopeMD, Scope));
+      AliasInstMDMap.insert(std::make_pair(std::make_pair(MF, ScopeMD), Scope));
       ScopeList.push_back(Scope);
     }
   }
@@ -2308,7 +2311,8 @@ MachineInstr *SPIRVGlobalRegistry::getOrAddMemAliasingINTELInst(
   for (auto *Scope : ScopeList)
     MIB.addUse(Scope->getOperand(0).getReg());
   auto List = MIB.getInstr();
-  AliasInstMDMap.insert(std::make_pair(AliasingListMD, List));
+  AliasInstMDMap.insert(
+      std::make_pair(std::make_pair(MF, AliasingListMD), List));
   return List;
 }
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
index 37911c1bffd88..af2d3bf08207b 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
@@ -97,8 +97,11 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping {
   // Maps OpVariable and OpFunction-related v-regs to its LLVM IR definition.
   DenseMap<std::pair<const MachineFunction *, Register>, const Value *> Reg2GO;
 
-  // map of aliasing decorations to aliasing metadata
-  DenseMap<const MDNode *, MachineInstr *> AliasInstMDMap;
+  // map of aliasing decorations to aliasing metadata, keyed per
+  // MachineFunction: the cached instructions define virtual registers, which
+  // are only valid in the function that created them.
+  DenseMap<std::pair<const MachineFunction *, const MDNode *>, MachineInstr *>
+      AliasInstMDMap;
 
   // Add a new OpTypeXXX instruction without checking for duplicates.
   SPIRVTypeInst createSPIRVType(const Type *Type, MachineIRBuilder &MIRBuilder,
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_memory_access_aliasing/alias-scope-shared-across-functions.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_memory_access_aliasing/alias-scope-shared-across-functions.ll
new file mode 100644
index 0000000000000..34349bdd4de3c
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_memory_access_aliasing/alias-scope-shared-across-functions.ll
@@ -0,0 +1,46 @@
+; Check that an alias scope list referenced from two functions is declared in
+; each of them. The aliasing instructions are cached per MDNode, but the virtual
+; registers they define belong to one MachineFunction, so a cache shared across
+; functions makes the second function reference a register that does not exist
+; in it.
+
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown -verify-machineinstrs --spirv-ext=+SPV_INTEL_memory_access_aliasing %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_memory_access_aliasing %s -o - -filetype=obj | spirv-val %}
+
+; CHECK: OpCapability MemoryAccessAliasingINTEL
+; CHECK: OpExtension "SPV_INTEL_memory_access_aliasing"
+
+; Both functions use !1, so each declares its own domain, scope and list.
+; CHECK: %[[#Domain1:]] = OpAliasDomainDeclINTEL
+; CHECK: %[[#Domain2:]] = OpAliasDomainDeclINTEL
+; CHECK: %[[#Scope1:]] = OpAliasScopeDeclINTEL %[[#Domain1]]
+; CHECK: %[[#Scope2:]] = OpAliasScopeDeclINTEL %[[#Domain2]]
+; CHECK: %[[#List1:]] = OpAliasScopeListDeclINTEL %[[#Scope1]]
+; CHECK: %[[#List2:]] = OpAliasScopeListDeclINTEL %[[#Scope2]]
+
+; One load per function, each using a different one of the two lists. Which
+; function gets which id is not fixed, so match them in either order.
+; CHECK-DAG: OpLoad %[[#]] %[[#]] Aligned|AliasScopeINTELMask 4 %[[#List1]]
+; CHECK-DAG: OpLoad %[[#]] %[[#]] Aligned|AliasScopeINTELMask 4 %[[#List2]]
+
+define spir_kernel void @foo(ptr addrspace(1) noalias %in, ptr addrspace(1) noalias %out) {
+entry:
+  %0 = addrspacecast ptr addrspace(1) %in to ptr addrspace(4)
+  %1 = addrspacecast ptr addrspace(1) %out to ptr addrspace(4)
+  %v = load i32, ptr addrspace(4) %0, align 4, !alias.scope !1
+  store i32 %v, ptr addrspace(4) %1, align 4
+  ret void
+}
+
+define spir_kernel void @bar(ptr addrspace(1) noalias %in, ptr addrspace(1) noalias %out) {
+entry:
+  %0 = addrspacecast ptr addrspace(1) %in to ptr addrspace(4)
+  %1 = addrspacecast ptr addrspace(1) %out to ptr addrspace(4)
+  %v = load i32, ptr addrspace(4) %0, align 4, !alias.scope !1
+  store i32 %v, ptr addrspace(4) %1, align 4
+  ret void
+}
+
+!1 = !{!2}
+!2 = distinct !{!2, !3, !"shared: %in"}
+!3 = distinct !{!3, !"shared"}



More information about the llvm-commits mailing list