[llvm] [SPIRV] Add legalization pass for zero-size arrays (PR #172367)

Nick Sarnie via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 17 08:50:36 PST 2025


https://github.com/sarnex updated https://github.com/llvm/llvm-project/pull/172367

>From 1aeaa139911936c18932f2b8c3c6887e3ff18756 Mon Sep 17 00:00:00 2001
From: Nick Sarnie <nick.sarnie at intel.com>
Date: Fri, 12 Dec 2025 14:09:44 -0800
Subject: [PATCH 1/4] [SPIRV] Add legalization pass for zero-size arrays

Signed-off-by: Nick Sarnie <nick.sarnie at intel.com>
---
 llvm/lib/Target/SPIRV/CMakeLists.txt          |   1 +
 llvm/lib/Target/SPIRV/SPIRV.h                 |   2 +
 .../SPIRV/SPIRVLegalizeZeroSizeArrays.cpp     | 334 ++++++++++++++++++
 .../SPIRV/SPIRVLegalizeZeroSizeArrays.h       |  24 ++
 llvm/lib/Target/SPIRV/SPIRVPassRegistry.def   |   1 +
 llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp  |   3 +
 .../legalize-zero-size-arrays-alloca-count.ll |  12 +
 .../SPIRV/legalize-zero-size-arrays-alloca.ll |  14 +
 .../legalize-zero-size-arrays-extractvalue.ll |  12 +
 .../SPIRV/legalize-zero-size-arrays-global.ll |   8 +
 .../legalize-zero-size-arrays-insertvalue.ll  |  16 +
 .../SPIRV/legalize-zero-size-arrays-load.ll   |  13 +
 .../SPIRV/legalize-zero-size-arrays-nested.ll |   8 +
 .../SPIRV/legalize-zero-size-arrays-select.ll |  12 +
 .../SPIRV/legalize-zero-size-arrays-store.ll  |  13 +
 .../SPIRV/legalize-zero-size-arrays-struct.ll |  11 +
 llvm/test/CodeGen/SPIRV/llc-pipeline.ll       |   2 +
 llvm/test/CodeGen/SPIRV/zero-length-array.ll  |   2 +-
 18 files changed, 487 insertions(+), 1 deletion(-)
 create mode 100644 llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
 create mode 100644 llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.h
 create mode 100644 llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca-count.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-extractvalue.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-global.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-insertvalue.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-load.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-nested.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-select.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-store.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-struct.ll

diff --git a/llvm/lib/Target/SPIRV/CMakeLists.txt b/llvm/lib/Target/SPIRV/CMakeLists.txt
index 79b76165cd57a..bb8307c2d2d36 100644
--- a/llvm/lib/Target/SPIRV/CMakeLists.txt
+++ b/llvm/lib/Target/SPIRV/CMakeLists.txt
@@ -27,6 +27,7 @@ add_llvm_target(SPIRVCodeGen
   SPIRVInstrInfo.cpp
   SPIRVInstructionSelector.cpp
   SPIRVLegalizeImplicitBinding.cpp
+  SPIRVLegalizeZeroSizeArrays.cpp
   SPIRVStripConvergentIntrinsics.cpp
   SPIRVLegalizePointerCast.cpp
   SPIRVMergeRegionExitTargets.cpp
diff --git a/llvm/lib/Target/SPIRV/SPIRV.h b/llvm/lib/Target/SPIRV/SPIRV.h
index fa85ee781c249..b6188adfdb5b3 100644
--- a/llvm/lib/Target/SPIRV/SPIRV.h
+++ b/llvm/lib/Target/SPIRV/SPIRV.h
@@ -25,6 +25,7 @@ ModulePass *createSPIRVCBufferAccessLegacyPass();
 FunctionPass *createSPIRVMergeRegionExitTargetsPass();
 FunctionPass *createSPIRVStripConvergenceIntrinsicsPass();
 ModulePass *createSPIRVLegalizeImplicitBindingPass();
+ModulePass *createSPIRVLegalizeZeroSizeArraysPass();
 FunctionPass *createSPIRVLegalizePointerCastPass(SPIRVTargetMachine *TM);
 FunctionPass *createSPIRVRegularizerPass();
 FunctionPass *createSPIRVPreLegalizerCombiner();
@@ -55,6 +56,7 @@ void initializeSPIRVPrepareFunctionsPass(PassRegistry &);
 void initializeSPIRVPrepareGlobalsPass(PassRegistry &);
 void initializeSPIRVStripConvergentIntrinsicsPass(PassRegistry &);
 void initializeSPIRVLegalizeImplicitBindingPass(PassRegistry &);
+void initializeSPIRVLegalizeZeroSizeArraysLegacyPass(PassRegistry &);
 } // namespace llvm
 
 #endif // LLVM_LIB_TARGET_SPIRV_SPIRV_H
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
new file mode 100644
index 0000000000000..a1eb6ca3f6aa3
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
@@ -0,0 +1,334 @@
+//===- SPIRVLegalizeZeroSizeArrays.cpp - Legalize zero-size arrays -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// SPIR-V does not support zero-size arrays unless it is within a shader. This
+// pass legalizes zero-size arrays ([0 x T]) in unsupported cases.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SPIRVLegalizeZeroSizeArrays.h"
+#include "SPIRV.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/InstVisitor.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/TargetParser/Triple.h"
+
+#define DEBUG_TYPE "spirv-legalize-zero-size-arrays"
+
+using namespace llvm;
+
+namespace {
+
+bool hasZeroSizeArray(const Type *Ty) {
+  if (const ArrayType *ArrTy = dyn_cast<ArrayType>(Ty)) {
+    if (ArrTy->getNumElements() == 0)
+      return true;
+    return hasZeroSizeArray(ArrTy->getElementType());
+  }
+
+  if (const StructType *StructTy = dyn_cast<StructType>(Ty)) {
+    for (Type *ElemTy : StructTy->elements()) {
+      if (hasZeroSizeArray(ElemTy))
+        return true;
+    }
+  }
+
+  return false;
+}
+
+class SPIRVLegalizeZeroSizeArraysImpl
+    : public InstVisitor<SPIRVLegalizeZeroSizeArraysImpl> {
+  friend class InstVisitor<SPIRVLegalizeZeroSizeArraysImpl>;
+
+public:
+  bool runOnModule(Module &M);
+
+  // TODO: Handle GEP, PHI
+  void visitAllocaInst(AllocaInst &AI);
+  void visitLoadInst(LoadInst &LI);
+  void visitStoreInst(StoreInst &SI);
+  void visitSelectInst(SelectInst &Sel);
+  void visitExtractValueInst(ExtractValueInst &EVI);
+  void visitInsertValueInst(InsertValueInst &IVI);
+
+private:
+  Type *legalizeType(Type *Ty);
+  Constant *legalizeConstant(Constant *C);
+
+  DenseMap<Type *, Type *> TypeMap;
+  DenseMap<GlobalVariable *, GlobalVariable *> GlobalMap;
+  SmallVector<Instruction *, 16> ToErase;
+  bool Modified = false;
+};
+
+class SPIRVLegalizeZeroSizeArraysLegacy : public ModulePass {
+public:
+  static char ID;
+  SPIRVLegalizeZeroSizeArraysLegacy() : ModulePass(ID) {}
+  StringRef getPassName() const override {
+    return "SPIRV Legalize Zero-Size Arrays";
+  }
+  bool runOnModule(Module &M) override {
+    SPIRVLegalizeZeroSizeArraysImpl Impl;
+    return Impl.runOnModule(M);
+  }
+};
+
+Type *SPIRVLegalizeZeroSizeArraysImpl::legalizeType(Type *Ty) {
+  auto It = TypeMap.find(Ty);
+  if (It != TypeMap.end())
+    return It->second;
+
+  Type *LegalizedTy = Ty;
+
+  if (ArrayType *ArrTy = dyn_cast<ArrayType>(Ty)) {
+    if (ArrTy->getNumElements() == 0) {
+      LegalizedTy = PointerType::getUnqual(Ty->getContext());
+    } else if (Type *ElemTy = legalizeType(ArrTy->getElementType());
+               ElemTy != ArrTy->getElementType()) {
+      LegalizedTy = ArrayType::get(ElemTy, ArrTy->getNumElements());
+    }
+  } else if (StructType *StructTy = dyn_cast<StructType>(Ty)) {
+    SmallVector<Type *, 8> ElemTypes;
+    bool Changed = false;
+    for (Type *ElemTy : StructTy->elements()) {
+      Type *LegalizedElemTy = legalizeType(ElemTy);
+      ElemTypes.push_back(LegalizedElemTy);
+      Changed |= LegalizedElemTy != ElemTy;
+    }
+    if (Changed) {
+      LegalizedTy =
+          StructTy->hasName()
+              ? StructType::create(StructTy->getContext(), ElemTypes,
+                                   (StructTy->getName() + ".legalized").str(),
+                                   StructTy->isPacked())
+              : StructType::get(StructTy->getContext(), ElemTypes,
+                                StructTy->isPacked());
+    }
+  }
+
+  TypeMap[Ty] = LegalizedTy;
+  return LegalizedTy;
+}
+
+Constant *SPIRVLegalizeZeroSizeArraysImpl::legalizeConstant(Constant *C) {
+  if (!C || !hasZeroSizeArray(C->getType()))
+    return C;
+
+  if (GlobalVariable *GV = dyn_cast<GlobalVariable>(C))
+    return GlobalMap.lookup(GV) ? GlobalMap[GV] : C;
+
+  Type *NewTy = legalizeType(C->getType());
+  if (isa<UndefValue>(C) || isa<PoisonValue>(C))
+    return PoisonValue::get(NewTy);
+  if (isa<ConstantAggregateZero>(C))
+    return Constant::getNullValue(NewTy);
+
+  if (ConstantArray *CA = dyn_cast<ConstantArray>(C)) {
+    SmallVector<Constant *, 8> Elems;
+    for (Use &U : CA->operands())
+      Elems.push_back(legalizeConstant(cast<Constant>(U)));
+    return ConstantArray::get(cast<ArrayType>(NewTy), Elems);
+  }
+
+  if (ConstantStruct *CS = dyn_cast<ConstantStruct>(C)) {
+    SmallVector<Constant *, 8> Fields;
+    for (Use &U : CS->operands())
+      Fields.push_back(legalizeConstant(cast<Constant>(U)));
+    return ConstantStruct::get(cast<StructType>(NewTy), Fields);
+  }
+
+  if (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) {
+    // Don't legalize GEP constant expressions, the backend deals with them
+    // fine.
+    if (CE->getOpcode() == Instruction::GetElementPtr)
+      return CE;
+    SmallVector<Constant *, 4> Ops;
+    bool Changed = false;
+    for (Use &U : CE->operands()) {
+      Constant *LegalizedOp = legalizeConstant(cast<Constant>(U));
+      Ops.push_back(LegalizedOp);
+      Changed |= LegalizedOp != cast<Constant>(U.get());
+    }
+    if (Changed)
+      return CE->getWithOperands(Ops);
+  }
+
+  return C;
+}
+
+void SPIRVLegalizeZeroSizeArraysImpl::visitAllocaInst(AllocaInst &AI) {
+  if (!hasZeroSizeArray(AI.getAllocatedType()))
+    return;
+
+  // TODO: Handle nested arrays and structs containing zero-size arrays
+  ArrayType *ArrTy = dyn_cast<ArrayType>(AI.getAllocatedType());
+  if (ArrTy && ArrTy->getNumElements() == 0) {
+    IRBuilder<> Builder(&AI);
+    AllocaInst *NewAI = Builder.CreateAlloca(ArrTy->getElementType(),
+                                             AI.getArraySize(), AI.getName());
+    NewAI->setAlignment(AI.getAlign());
+    NewAI->setDebugLoc(AI.getDebugLoc());
+    AI.replaceAllUsesWith(NewAI);
+    ToErase.push_back(&AI);
+    Modified = true;
+  }
+}
+
+void SPIRVLegalizeZeroSizeArraysImpl::visitLoadInst(LoadInst &LI) {
+  if (!hasZeroSizeArray(LI.getType()))
+    return;
+
+  // TODO: Handle nested arrays and structs containing zero-size arrays
+  ArrayType *ArrTy = dyn_cast<ArrayType>(LI.getType());
+  if (ArrTy && ArrTy->getNumElements() == 0) {
+    LI.replaceAllUsesWith(PoisonValue::get(LI.getType()));
+    ToErase.push_back(&LI);
+    Modified = true;
+  }
+}
+
+void SPIRVLegalizeZeroSizeArraysImpl::visitStoreInst(StoreInst &SI) {
+  Type *StoreTy = SI.getValueOperand()->getType();
+
+  // TODO: Handle nested arrays and structs containing zero-size arrays
+  ArrayType *ArrTy = dyn_cast<ArrayType>(StoreTy);
+  if (ArrTy && ArrTy->getNumElements() == 0) {
+    ToErase.push_back(&SI);
+    Modified = true;
+  }
+}
+
+void SPIRVLegalizeZeroSizeArraysImpl::visitSelectInst(SelectInst &Sel) {
+  if (!hasZeroSizeArray(Sel.getType()))
+    return;
+
+  // TODO: Handle nested arrays and structs containing zero-size arrays
+  ArrayType *ArrTy = dyn_cast<ArrayType>(Sel.getType());
+  if (ArrTy && ArrTy->getNumElements() == 0) {
+    Sel.replaceAllUsesWith(PoisonValue::get(Sel.getType()));
+    ToErase.push_back(&Sel);
+    Modified = true;
+  }
+}
+
+void SPIRVLegalizeZeroSizeArraysImpl::visitExtractValueInst(
+    ExtractValueInst &EVI) {
+  if (!hasZeroSizeArray(EVI.getAggregateOperand()->getType()))
+    return;
+
+  // TODO: Handle nested arrays and structs containing zero-size arrays
+  ArrayType *ArrTy = dyn_cast<ArrayType>(EVI.getType());
+  if (ArrTy && ArrTy->getNumElements() == 0) {
+    EVI.replaceAllUsesWith(PoisonValue::get(EVI.getType()));
+    ToErase.push_back(&EVI);
+    Modified = true;
+  }
+}
+
+void SPIRVLegalizeZeroSizeArraysImpl::visitInsertValueInst(
+    InsertValueInst &IVI) {
+  if (!hasZeroSizeArray(IVI.getAggregateOperand()->getType()))
+    return;
+
+  // TODO: Handle nested arrays and structs containing zero-size arrays
+  ArrayType *ArrTy =
+      dyn_cast<ArrayType>(IVI.getInsertedValueOperand()->getType());
+  if (ArrTy && ArrTy->getNumElements() == 0) {
+    IVI.replaceAllUsesWith(IVI.getAggregateOperand());
+    ToErase.push_back(&IVI);
+    Modified = true;
+  }
+}
+
+bool SPIRVLegalizeZeroSizeArraysImpl::runOnModule(Module &M) {
+  TypeMap.clear();
+  GlobalMap.clear();
+  ToErase.clear();
+  Modified = false;
+
+  // Runtime arrays are allowed for shaders, so we don't need to do anything.
+  Triple Triple(M.getTargetTriple());
+  if (Triple.getOS() == Triple::Vulkan)
+    return false;
+
+  // First pass: create new globals and track mapping (don't erase old ones
+  // yet).
+  SmallVector<GlobalVariable *, 8> OldGlobals;
+  for (GlobalVariable &GV : M.globals()) {
+    if (!hasZeroSizeArray(GV.getValueType()))
+      continue;
+
+    // llvm.embedded.module is handled by SPIRVPrepareGlobals
+    if (GV.getName() == "llvm.embedded.module")
+      continue;
+
+    Type *NewTy = legalizeType(GV.getValueType());
+    // Use an empty name and initializer for now, we will update them in the
+    // following steps.
+    GlobalVariable *NewGV = new GlobalVariable(
+        M, NewTy, GV.isConstant(), GV.getLinkage(), /*Initializer=*/nullptr,
+        /*Name=*/"", &GV, GV.getThreadLocalMode(), GV.getAddressSpace(),
+        GV.isExternallyInitialized());
+    NewGV->copyAttributesFrom(&GV);
+    NewGV->copyMetadata(&GV, 0);
+    NewGV->setComdat(GV.getComdat());
+    NewGV->setAlignment(GV.getAlign());
+    GlobalMap[&GV] = NewGV;
+    OldGlobals.push_back(&GV);
+    Modified = true;
+  }
+
+  // Second pass: set initializers now that all globals are mapped.
+  for (GlobalVariable *GV : OldGlobals) {
+    GlobalVariable *NewGV = cast<GlobalVariable>(GlobalMap[GV]);
+    if (GV->hasInitializer())
+      NewGV->setInitializer(legalizeConstant(GV->getInitializer()));
+  }
+
+  // Third pass: replace uses, transfer names, and erase old globals.
+  for (GlobalVariable *GV : OldGlobals) {
+    GlobalVariable *NewGV = GlobalMap[GV];
+    GV->replaceAllUsesWith(ConstantExpr::getBitCast(NewGV, GV->getType()));
+    NewGV->takeName(GV);
+    GV->eraseFromParent();
+  }
+
+  for (Function &F : M)
+    for (Instruction &I : instructions(F))
+      visit(I);
+
+  for (Instruction *I : ToErase)
+    I->eraseFromParent();
+
+  return Modified;
+}
+
+} // namespace
+
+PreservedAnalyses SPIRVLegalizeZeroSizeArrays::run(Module &M,
+                                                   ModuleAnalysisManager &AM) {
+  SPIRVLegalizeZeroSizeArraysImpl Impl;
+  if (Impl.runOnModule(M))
+    return PreservedAnalyses::none();
+  return PreservedAnalyses::all();
+}
+
+char SPIRVLegalizeZeroSizeArraysLegacy::ID = 0;
+
+INITIALIZE_PASS(SPIRVLegalizeZeroSizeArraysLegacy,
+                "spirv-legalize-zero-size-arrays",
+                "Legalize SPIR-V zero-size arrays", false, false)
+
+ModulePass *llvm::createSPIRVLegalizeZeroSizeArraysPass() {
+  return new SPIRVLegalizeZeroSizeArraysLegacy();
+}
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.h b/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.h
new file mode 100644
index 0000000000000..7fe653c4a0d78
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.h
@@ -0,0 +1,24 @@
+//===- SPIRVLegalizeZeroSizeArrays.h - Legalize zero-size arrays *- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVLEGALIZEZEROSIZE_ARRAYS_H_
+#define LLVM_LIB_TARGET_SPIRV_SPIRVLEGALIZEZEROSIZE_ARRAYS_H_
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class SPIRVLegalizeZeroSizeArrays
+    : public PassInfoMixin<SPIRVLegalizeZeroSizeArrays> {
+public:
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+};
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_SPIRV_SPIRVLEGALIZEZEROSIZE_ARRAYS_H_
diff --git a/llvm/lib/Target/SPIRV/SPIRVPassRegistry.def b/llvm/lib/Target/SPIRV/SPIRVPassRegistry.def
index 1ce131fe7b1bf..90dac17ec54b2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPassRegistry.def
+++ b/llvm/lib/Target/SPIRV/SPIRVPassRegistry.def
@@ -17,6 +17,7 @@
 #define MODULE_PASS(NAME, CREATE_PASS)
 #endif
 MODULE_PASS("spirv-cbuffer-access", SPIRVCBufferAccess())
+MODULE_PASS("spirv-legalize-zero-size-arrays", SPIRVLegalizeZeroSizeArrays())
 #undef MODULE_PASS
 
 #ifndef FUNCTION_PASS
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index 10bbca225b20a..86bef3255f532 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -14,6 +14,7 @@
 #include "SPIRV.h"
 #include "SPIRVCBufferAccess.h"
 #include "SPIRVGlobalRegistry.h"
+#include "SPIRVLegalizeZeroSizeArrays.h"
 #include "SPIRVLegalizerInfo.h"
 #include "SPIRVStructurizerWrapper.h"
 #include "SPIRVTargetObjectFile.h"
@@ -52,6 +53,7 @@ extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() {
   initializeSPIRVCBufferAccessLegacyPass(PR);
   initializeSPIRVPreLegalizerCombinerPass(PR);
   initializeSPIRVLegalizePointerCastPass(PR);
+  initializeSPIRVLegalizeZeroSizeArraysLegacyPass(PR);
   initializeSPIRVRegularizerPass(PR);
   initializeSPIRVPreLegalizerPass(PR);
   initializeSPIRVPostLegalizerPass(PR);
@@ -210,6 +212,7 @@ void SPIRVPassConfig::addISelPrepare() {
 
   addPass(createSPIRVStripConvergenceIntrinsicsPass());
   addPass(createSPIRVLegalizeImplicitBindingPass());
+  addPass(createSPIRVLegalizeZeroSizeArraysPass());
   addPass(createSPIRVCBufferAccessLegacyPass());
   addPass(createSPIRVEmitIntrinsicsPass(&getTM<SPIRVTargetMachine>()));
   if (TM.getSubtargetImpl()->isLogicalSPIRV())
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca-count.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca-count.ll
new file mode 100644
index 0000000000000..2d9aa12fce8d1
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca-count.ll
@@ -0,0 +1,12 @@
+; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown -spirv-ext=+SPV_INTEL_variable_length_array %s -o - -filetype=obj | spirv-val %}
+
+; Test that zero-size array alloca with dynamic count allocates element type with count
+
+define void @test_alloca_with_count(i32 %n) {
+; CHECK-LABEL: @test_alloca_with_count(
+; CHECK-NEXT:    [[ARR:%.*]] = alloca i32, i32 [[N:%.*]], align 4
+; CHECK-NEXT:    ret void
+  %arr = alloca [0 x i32], i32 %n, align 4
+  ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca.ll
new file mode 100644
index 0000000000000..58d6453a71c7e
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca.ll
@@ -0,0 +1,14 @@
+; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Test that alloca of zero-size array allocates element type instead
+
+define void @test_alloca_zero_array() {
+; CHECK-LABEL: @test_alloca_zero_array(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ARR:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    ret void
+entry:
+  %arr = alloca [0 x i32], align 4
+  ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-extractvalue.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-extractvalue.ll
new file mode 100644
index 0000000000000..b1e7968c7603d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-extractvalue.ll
@@ -0,0 +1,12 @@
+; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+
+; Test that extractvalue of zero-size array is replaced with poison
+
+; Can't run spirv-val as function signatures aren't handled
+
+define [0 x i32] @test_extractvalue_zero_array() {
+; CHECK-LABEL: @test_extractvalue_zero_array(
+; CHECK-NEXT:    ret [0 x i32] poison
+  %arr = extractvalue [1 x [0 x i32]] zeroinitializer, 0
+  ret [0 x i32] %arr
+}
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-global.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-global.ll
new file mode 100644
index 0000000000000..e90478d4c86d6
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-global.ll
@@ -0,0 +1,8 @@
+; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Test that a global variable with zero-size array is transformed to ptr type
+
+ at global_zero_array = global [0 x i32] zeroinitializer
+
+; CHECK: @global_zero_array = global ptr null
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-insertvalue.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-insertvalue.ll
new file mode 100644
index 0000000000000..b20b84190b092
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-insertvalue.ll
@@ -0,0 +1,16 @@
+; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+
+; Test that insertvalue of zero-size array is removed
+
+%struct.with_zero = type { i32, [0 x i32], i64 }
+
+define void @test_insertvalue_zero_array(ptr %ptr, %struct.with_zero %s) {
+; CHECK-LABEL: @test_insertvalue_zero_array(
+; CHECK-NEXT:    [[AGG:%.*]] = insertvalue %struct.with_zero %s, i32 42, 0
+; CHECK-NEXT:    store %struct.with_zero [[AGG]], ptr %ptr
+; CHECK-NEXT:    ret void
+  %agg = insertvalue %struct.with_zero %s, i32 42, 0
+  %result = insertvalue %struct.with_zero %agg, [0 x i32] zeroinitializer, 1
+  store %struct.with_zero %result, ptr %ptr
+  ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-load.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-load.ll
new file mode 100644
index 0000000000000..4209fc27d579d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-load.ll
@@ -0,0 +1,13 @@
+; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Test that load of zero-size array is replaced with poison and removed
+
+define void @test_load_zero_array(ptr %ptr) {
+; CHECK-LABEL: @test_load_zero_array(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    ret void
+entry:
+  %val = load [0 x i32], ptr %ptr, align 4
+  ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-nested.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-nested.ll
new file mode 100644
index 0000000000000..45b24998321ce
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-nested.ll
@@ -0,0 +1,8 @@
+; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Test that nested zero-size arrays are legalized to pointers
+
+ at nested_zero_array = global [2 x [0 x i32]] zeroinitializer
+
+; CHECK: @nested_zero_array = global [2 x ptr] zeroinitializer
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-select.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-select.ll
new file mode 100644
index 0000000000000..45cb959058078
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-select.ll
@@ -0,0 +1,12 @@
+; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+
+; Test that select of zero-size array is replaced with poison
+
+; Can't run spirv-val as function signatures are not handled
+
+define [0 x i32] @test_select_zero_array(i1 %cond, [0 x i32] %a) {
+; CHECK-LABEL: @test_select_zero_array(
+; CHECK-NEXT:    ret [0 x i32] poison
+  %result = select i1 %cond, [0 x i32] zeroinitializer, [0 x i32] %a
+  ret [0 x i32] %result
+}
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-store.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-store.ll
new file mode 100644
index 0000000000000..5fe1ef8affaa7
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-store.ll
@@ -0,0 +1,13 @@
+; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Test that store of zero-size array is removed
+
+define void @test_store_zero_array(ptr %ptr) {
+; CHECK-LABEL: @test_store_zero_array(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    ret void
+entry:
+  store [0 x i32] zeroinitializer, ptr %ptr, align 4
+  ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-struct.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-struct.ll
new file mode 100644
index 0000000000000..e2210a772c4fd
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-struct.ll
@@ -0,0 +1,11 @@
+; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Test that struct with zero-size array field becomes pointer
+
+%struct.with_zero = type { i32, [0 x i32], i32 }
+
+ at global_struct = global %struct.with_zero zeroinitializer
+
+; CHECK: %struct.with_zero.legalized = type { i32, ptr, i32 }
+; CHECK: @global_struct = global %struct.with_zero.legalized zeroinitializer
diff --git a/llvm/test/CodeGen/SPIRV/llc-pipeline.ll b/llvm/test/CodeGen/SPIRV/llc-pipeline.ll
index 3a1d0f7b5d218..25fcc8ffd18e4 100644
--- a/llvm/test/CodeGen/SPIRV/llc-pipeline.ll
+++ b/llvm/test/CodeGen/SPIRV/llc-pipeline.ll
@@ -39,6 +39,7 @@
 ; SPIRV-O0-NEXT:      Remove unreachable blocks from the CFG
 ; SPIRV-O0-NEXT:      SPIRV strip convergent intrinsics
 ; SPIRV-O0-NEXT:    SPIRV Legalize Implicit Binding
+; SPIRV-O0-NEXT:    SPIRV Legalize Zero-Size Arrays
 ; SPIRV-O0-NEXT:    SPIRV CBuffer Access
 ; SPIRV-O0-NEXT:    SPIRV emit intrinsics
 ; SPIRV-O0-NEXT:    FunctionPass Manager
@@ -144,6 +145,7 @@
 ; SPIRV-Opt-NEXT:      Remove unreachable blocks from the CFG
 ; SPIRV-Opt-NEXT:      SPIRV strip convergent intrinsics
 ; SPIRV-Opt-NEXT:    SPIRV Legalize Implicit Binding
+; SPIRV-Opt-NEXT:    SPIRV Legalize Zero-Size Arrays
 ; SPIRV-Opt-NEXT:    SPIRV CBuffer Access
 ; SPIRV-Opt-NEXT:    SPIRV emit intrinsics
 ; SPIRV-Opt-NEXT:    FunctionPass Manager
diff --git a/llvm/test/CodeGen/SPIRV/zero-length-array.ll b/llvm/test/CodeGen/SPIRV/zero-length-array.ll
index cb34529ebfecd..70936e0515b2b 100644
--- a/llvm/test/CodeGen/SPIRV/zero-length-array.ll
+++ b/llvm/test/CodeGen/SPIRV/zero-length-array.ll
@@ -19,6 +19,6 @@
 
 define spir_func void @foo() {
 entry:
-  %i = alloca [0 x i32], align 4
+  %i = alloca [0 x [ 0 x i32]], align 4
   ret void
 }

>From 15ebb98779203c04ad2030161aeb89c2451f41e8 Mon Sep 17 00:00:00 2001
From: Nick Sarnie <nick.sarnie at intel.com>
Date: Tue, 16 Dec 2025 09:07:55 -0800
Subject: [PATCH 2/4] address feedback

Signed-off-by: Nick Sarnie <nick.sarnie at intel.com>
---
 llvm/lib/Target/SPIRV/SPIRV.h                 |  2 +-
 .../SPIRV/SPIRVLegalizeZeroSizeArrays.cpp     | 80 ++++++++++++-------
 .../SPIRV/SPIRVLegalizeZeroSizeArrays.h       |  5 ++
 llvm/lib/Target/SPIRV/SPIRVPassRegistry.def   |  2 +-
 llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp  |  8 +-
 .../legalize-zero-size-arrays-alloca-count.ll |  4 +-
 ...legalize-zero-size-arrays-alloca-nested.ll | 11 +++
 .../SPIRV/legalize-zero-size-arrays-alloca.ll |  4 +-
 .../legalize-zero-size-arrays-extractvalue.ll |  5 +-
 .../SPIRV/legalize-zero-size-arrays-global.ll |  4 +-
 .../legalize-zero-size-arrays-insertvalue.ll  |  5 +-
 .../SPIRV/legalize-zero-size-arrays-load.ll   |  2 +-
 .../SPIRV/legalize-zero-size-arrays-nested.ll |  4 +-
 .../SPIRV/legalize-zero-size-arrays-select.ll |  5 +-
 .../SPIRV/legalize-zero-size-arrays-store.ll  |  2 +-
 .../SPIRV/legalize-zero-size-arrays-struct.ll |  4 +-
 llvm/test/CodeGen/SPIRV/zero-length-array.ll  |  4 +-
 17 files changed, 99 insertions(+), 52 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca-nested.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRV.h b/llvm/lib/Target/SPIRV/SPIRV.h
index b6188adfdb5b3..8d5233848307e 100644
--- a/llvm/lib/Target/SPIRV/SPIRV.h
+++ b/llvm/lib/Target/SPIRV/SPIRV.h
@@ -25,7 +25,7 @@ ModulePass *createSPIRVCBufferAccessLegacyPass();
 FunctionPass *createSPIRVMergeRegionExitTargetsPass();
 FunctionPass *createSPIRVStripConvergenceIntrinsicsPass();
 ModulePass *createSPIRVLegalizeImplicitBindingPass();
-ModulePass *createSPIRVLegalizeZeroSizeArraysPass();
+ModulePass *createSPIRVLegalizeZeroSizeArraysPass(const SPIRVTargetMachine *TM);
 FunctionPass *createSPIRVLegalizePointerCastPass(SPIRVTargetMachine *TM);
 FunctionPass *createSPIRVRegularizerPass();
 FunctionPass *createSPIRVPreLegalizerCombiner();
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
index a1eb6ca3f6aa3..41ef344903ddb 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
@@ -13,6 +13,8 @@
 
 #include "SPIRVLegalizeZeroSizeArrays.h"
 #include "SPIRV.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/IR/IRBuilder.h"
@@ -20,7 +22,6 @@
 #include "llvm/IR/InstVisitor.h"
 #include "llvm/Pass.h"
 #include "llvm/Support/Debug.h"
-#include "llvm/TargetParser/Triple.h"
 
 #define DEBUG_TYPE "spirv-legalize-zero-size-arrays"
 
@@ -45,14 +46,24 @@ bool hasZeroSizeArray(const Type *Ty) {
   return false;
 }
 
+bool shouldLegalizeInstType(const Type *Ty) {
+  if (const ArrayType *ArrTy = dyn_cast_if_present<ArrayType>(Ty)) {
+    return ArrTy->getNumElements() == 0 ||
+           shouldLegalizeInstType(ArrTy->getElementType());
+  }
+  return false;
+}
+
 class SPIRVLegalizeZeroSizeArraysImpl
     : public InstVisitor<SPIRVLegalizeZeroSizeArraysImpl> {
   friend class InstVisitor<SPIRVLegalizeZeroSizeArraysImpl>;
 
 public:
+  SPIRVLegalizeZeroSizeArraysImpl(const SPIRVTargetMachine *TM)
+      : InstVisitor(), TM(TM) {}
   bool runOnModule(Module &M);
 
-  // TODO: Handle GEP, PHI
+  // TODO: Handle GEP, PHI.
   void visitAllocaInst(AllocaInst &AI);
   void visitLoadInst(LoadInst &LI);
   void visitStoreInst(StoreInst &SI);
@@ -64,6 +75,7 @@ class SPIRVLegalizeZeroSizeArraysImpl
   Type *legalizeType(Type *Ty);
   Constant *legalizeConstant(Constant *C);
 
+  const SPIRVTargetMachine *TM;
   DenseMap<Type *, Type *> TypeMap;
   DenseMap<GlobalVariable *, GlobalVariable *> GlobalMap;
   SmallVector<Instruction *, 16> ToErase;
@@ -73,14 +85,18 @@ class SPIRVLegalizeZeroSizeArraysImpl
 class SPIRVLegalizeZeroSizeArraysLegacy : public ModulePass {
 public:
   static char ID;
-  SPIRVLegalizeZeroSizeArraysLegacy() : ModulePass(ID) {}
+  SPIRVLegalizeZeroSizeArraysLegacy(const SPIRVTargetMachine *TM)
+      : ModulePass(ID), TM(TM) {}
   StringRef getPassName() const override {
     return "SPIRV Legalize Zero-Size Arrays";
   }
   bool runOnModule(Module &M) override {
-    SPIRVLegalizeZeroSizeArraysImpl Impl;
+    SPIRVLegalizeZeroSizeArraysImpl Impl(TM);
     return Impl.runOnModule(M);
   }
+
+private:
+  const SPIRVTargetMachine *TM;
 };
 
 Type *SPIRVLegalizeZeroSizeArraysImpl::legalizeType(Type *Ty) {
@@ -92,7 +108,9 @@ Type *SPIRVLegalizeZeroSizeArraysImpl::legalizeType(Type *Ty) {
 
   if (ArrayType *ArrTy = dyn_cast<ArrayType>(Ty)) {
     if (ArrTy->getNumElements() == 0) {
-      LegalizedTy = PointerType::getUnqual(Ty->getContext());
+      LegalizedTy = PointerType::get(
+          Ty->getContext(),
+          storageClassToAddressSpace(SPIRV::StorageClass::Generic));
     } else if (Type *ElemTy = legalizeType(ArrTy->getElementType());
                ElemTy != ArrTy->getElementType()) {
       LegalizedTy = ArrayType::get(ElemTy, ArrTy->getNumElements());
@@ -124,11 +142,14 @@ Constant *SPIRVLegalizeZeroSizeArraysImpl::legalizeConstant(Constant *C) {
   if (!C || !hasZeroSizeArray(C->getType()))
     return C;
 
-  if (GlobalVariable *GV = dyn_cast<GlobalVariable>(C))
-    return GlobalMap.lookup(GV) ? GlobalMap[GV] : C;
+  if (GlobalVariable *GV = dyn_cast<GlobalVariable>(C)) {
+    if (GlobalVariable *NewGV = GlobalMap.lookup(GV))
+      return NewGV;
+    return C;
+  }
 
   Type *NewTy = legalizeType(C->getType());
-  if (isa<UndefValue>(C) || isa<PoisonValue>(C))
+  if (isa<UndefValue>(C))
     return PoisonValue::get(NewTy);
   if (isa<ConstantAggregateZero>(C))
     return Constant::getNullValue(NewTy);
@@ -170,12 +191,15 @@ void SPIRVLegalizeZeroSizeArraysImpl::visitAllocaInst(AllocaInst &AI) {
   if (!hasZeroSizeArray(AI.getAllocatedType()))
     return;
 
-  // TODO: Handle nested arrays and structs containing zero-size arrays
+  // TODO: Handle structs containing zero-size arrays.
   ArrayType *ArrTy = dyn_cast<ArrayType>(AI.getAllocatedType());
-  if (ArrTy && ArrTy->getNumElements() == 0) {
+  if (shouldLegalizeInstType(ArrTy)) {
     IRBuilder<> Builder(&AI);
-    AllocaInst *NewAI = Builder.CreateAlloca(ArrTy->getElementType(),
-                                             AI.getArraySize(), AI.getName());
+    AllocaInst *NewAI = Builder.CreateAlloca(
+        PointerType::get(
+            ArrTy->getContext(),
+            storageClassToAddressSpace(SPIRV::StorageClass::Generic)),
+        /*ArraySize=*/nullptr, AI.getName());
     NewAI->setAlignment(AI.getAlign());
     NewAI->setDebugLoc(AI.getDebugLoc());
     AI.replaceAllUsesWith(NewAI);
@@ -188,9 +212,9 @@ void SPIRVLegalizeZeroSizeArraysImpl::visitLoadInst(LoadInst &LI) {
   if (!hasZeroSizeArray(LI.getType()))
     return;
 
-  // TODO: Handle nested arrays and structs containing zero-size arrays
+  // TODO: Handle structs containing zero-size arrays.
   ArrayType *ArrTy = dyn_cast<ArrayType>(LI.getType());
-  if (ArrTy && ArrTy->getNumElements() == 0) {
+  if (shouldLegalizeInstType(ArrTy)) {
     LI.replaceAllUsesWith(PoisonValue::get(LI.getType()));
     ToErase.push_back(&LI);
     Modified = true;
@@ -200,9 +224,9 @@ void SPIRVLegalizeZeroSizeArraysImpl::visitLoadInst(LoadInst &LI) {
 void SPIRVLegalizeZeroSizeArraysImpl::visitStoreInst(StoreInst &SI) {
   Type *StoreTy = SI.getValueOperand()->getType();
 
-  // TODO: Handle nested arrays and structs containing zero-size arrays
+  // TODO: Handle structs containing zero-size arrays.
   ArrayType *ArrTy = dyn_cast<ArrayType>(StoreTy);
-  if (ArrTy && ArrTy->getNumElements() == 0) {
+  if (shouldLegalizeInstType(ArrTy)) {
     ToErase.push_back(&SI);
     Modified = true;
   }
@@ -212,9 +236,9 @@ void SPIRVLegalizeZeroSizeArraysImpl::visitSelectInst(SelectInst &Sel) {
   if (!hasZeroSizeArray(Sel.getType()))
     return;
 
-  // TODO: Handle nested arrays and structs containing zero-size arrays
+  // TODO: Handle structs containing zero-size arrays.
   ArrayType *ArrTy = dyn_cast<ArrayType>(Sel.getType());
-  if (ArrTy && ArrTy->getNumElements() == 0) {
+  if (shouldLegalizeInstType(ArrTy)) {
     Sel.replaceAllUsesWith(PoisonValue::get(Sel.getType()));
     ToErase.push_back(&Sel);
     Modified = true;
@@ -226,9 +250,9 @@ void SPIRVLegalizeZeroSizeArraysImpl::visitExtractValueInst(
   if (!hasZeroSizeArray(EVI.getAggregateOperand()->getType()))
     return;
 
-  // TODO: Handle nested arrays and structs containing zero-size arrays
+  // TODO: Handle structs containing zero-size arrays.
   ArrayType *ArrTy = dyn_cast<ArrayType>(EVI.getType());
-  if (ArrTy && ArrTy->getNumElements() == 0) {
+  if (shouldLegalizeInstType(ArrTy)) {
     EVI.replaceAllUsesWith(PoisonValue::get(EVI.getType()));
     ToErase.push_back(&EVI);
     Modified = true;
@@ -240,10 +264,10 @@ void SPIRVLegalizeZeroSizeArraysImpl::visitInsertValueInst(
   if (!hasZeroSizeArray(IVI.getAggregateOperand()->getType()))
     return;
 
-  // TODO: Handle nested arrays and structs containing zero-size arrays
+  // TODO: Handle structs containing zero-size arrays.
   ArrayType *ArrTy =
       dyn_cast<ArrayType>(IVI.getInsertedValueOperand()->getType());
-  if (ArrTy && ArrTy->getNumElements() == 0) {
+  if (shouldLegalizeInstType(ArrTy)) {
     IVI.replaceAllUsesWith(IVI.getAggregateOperand());
     ToErase.push_back(&IVI);
     Modified = true;
@@ -257,8 +281,7 @@ bool SPIRVLegalizeZeroSizeArraysImpl::runOnModule(Module &M) {
   Modified = false;
 
   // Runtime arrays are allowed for shaders, so we don't need to do anything.
-  Triple Triple(M.getTargetTriple());
-  if (Triple.getOS() == Triple::Vulkan)
+  if (TM && TM->getSubtargetImpl()->isShader())
     return false;
 
   // First pass: create new globals and track mapping (don't erase old ones
@@ -268,7 +291,7 @@ bool SPIRVLegalizeZeroSizeArraysImpl::runOnModule(Module &M) {
     if (!hasZeroSizeArray(GV.getValueType()))
       continue;
 
-    // llvm.embedded.module is handled by SPIRVPrepareGlobals
+    // llvm.embedded.module is handled by SPIRVPrepareGlobals.
     if (GV.getName() == "llvm.embedded.module")
       continue;
 
@@ -317,7 +340,7 @@ bool SPIRVLegalizeZeroSizeArraysImpl::runOnModule(Module &M) {
 
 PreservedAnalyses SPIRVLegalizeZeroSizeArrays::run(Module &M,
                                                    ModuleAnalysisManager &AM) {
-  SPIRVLegalizeZeroSizeArraysImpl Impl;
+  SPIRVLegalizeZeroSizeArraysImpl Impl(TM);
   if (Impl.runOnModule(M))
     return PreservedAnalyses::none();
   return PreservedAnalyses::all();
@@ -329,6 +352,7 @@ INITIALIZE_PASS(SPIRVLegalizeZeroSizeArraysLegacy,
                 "spirv-legalize-zero-size-arrays",
                 "Legalize SPIR-V zero-size arrays", false, false)
 
-ModulePass *llvm::createSPIRVLegalizeZeroSizeArraysPass() {
-  return new SPIRVLegalizeZeroSizeArraysLegacy();
+ModulePass *
+llvm::createSPIRVLegalizeZeroSizeArraysPass(const SPIRVTargetMachine *TM) {
+  return new SPIRVLegalizeZeroSizeArraysLegacy(TM);
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.h b/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.h
index 7fe653c4a0d78..b9b4be8032578 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.h
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.h
@@ -13,9 +13,14 @@
 
 namespace llvm {
 
+class SPIRVTargetMachine;
+
 class SPIRVLegalizeZeroSizeArrays
     : public PassInfoMixin<SPIRVLegalizeZeroSizeArrays> {
+  const SPIRVTargetMachine *TM;
+
 public:
+  SPIRVLegalizeZeroSizeArrays(const SPIRVTargetMachine *TM) : TM(TM) {}
   PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
 };
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVPassRegistry.def b/llvm/lib/Target/SPIRV/SPIRVPassRegistry.def
index 90dac17ec54b2..eba4ac02a0016 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPassRegistry.def
+++ b/llvm/lib/Target/SPIRV/SPIRVPassRegistry.def
@@ -17,7 +17,7 @@
 #define MODULE_PASS(NAME, CREATE_PASS)
 #endif
 MODULE_PASS("spirv-cbuffer-access", SPIRVCBufferAccess())
-MODULE_PASS("spirv-legalize-zero-size-arrays", SPIRVLegalizeZeroSizeArrays())
+MODULE_PASS("spirv-legalize-zero-size-arrays", SPIRVLegalizeZeroSizeArrays(/*TargetMachine=*/nullptr))
 #undef MODULE_PASS
 
 #ifndef FUNCTION_PASS
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index 86bef3255f532..0ea1f9a711962 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -209,14 +209,14 @@ void SPIRVPassConfig::addISelPrepare() {
     // back to virtual registers.
     addPass(createPromoteMemoryToRegisterPass());
   }
-
+  SPIRVTargetMachine &TM = getTM<SPIRVTargetMachine>();
   addPass(createSPIRVStripConvergenceIntrinsicsPass());
   addPass(createSPIRVLegalizeImplicitBindingPass());
-  addPass(createSPIRVLegalizeZeroSizeArraysPass());
+  addPass(createSPIRVLegalizeZeroSizeArraysPass(&TM));
   addPass(createSPIRVCBufferAccessLegacyPass());
-  addPass(createSPIRVEmitIntrinsicsPass(&getTM<SPIRVTargetMachine>()));
+  addPass(createSPIRVEmitIntrinsicsPass(&TM));
   if (TM.getSubtargetImpl()->isLogicalSPIRV())
-    addPass(createSPIRVLegalizePointerCastPass(&getTM<SPIRVTargetMachine>()));
+    addPass(createSPIRVLegalizePointerCastPass(&TM));
   TargetPassConfig::addISelPrepare();
 }
 
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca-count.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca-count.ll
index 2d9aa12fce8d1..553b54746b5b6 100644
--- a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca-count.ll
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca-count.ll
@@ -1,11 +1,11 @@
 ; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown -spirv-ext=+SPV_INTEL_variable_length_array %s -o - -filetype=obj | spirv-val %}
 
-; Test that zero-size array alloca with dynamic count allocates element type with count
+; Test that zero-size array alloca with dynamic count allocates element type with count.
 
 define void @test_alloca_with_count(i32 %n) {
 ; CHECK-LABEL: @test_alloca_with_count(
-; CHECK-NEXT:    [[ARR:%.*]] = alloca i32, i32 [[N:%.*]], align 4
+; CHECK-NEXT:    [[ARR:%.*]] = alloca ptr addrspace(4), align 4
 ; CHECK-NEXT:    ret void
   %arr = alloca [0 x i32], i32 %n, align 4
   ret void
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca-nested.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca-nested.ll
new file mode 100644
index 0000000000000..8e8f75aae2d02
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca-nested.ll
@@ -0,0 +1,11 @@
+; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown -spirv-ext=+SPV_INTEL_variable_length_array %s -o - -filetype=obj | spirv-val %}
+
+; Test that zero-size array alloca with nested arrays allocates element type.
+define ptr @test_alloca_with_count(i32 %n) {
+; CHECK-LABEL: @test_alloca_with_count(
+; CHECK-NEXT:    [[ARR:%.*]] = alloca ptr addrspace(4), align 4
+; CHECK-NEXT:    ret ptr [[ARR]]
+  %arr = alloca [0 x [0 x [0 x i32] ] ], align 4
+  ret ptr %arr
+}
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca.ll
index 58d6453a71c7e..2bf05bc249214 100644
--- a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca.ll
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca.ll
@@ -1,12 +1,12 @@
 ; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
-; Test that alloca of zero-size array allocates element type instead
+; Test that alloca of zero-size array allocates element type instead.
 
 define void @test_alloca_zero_array() {
 ; CHECK-LABEL: @test_alloca_zero_array(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[ARR:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[ARR:%.*]] = alloca ptr addrspace(4), align 4
 ; CHECK-NEXT:    ret void
 entry:
   %arr = alloca [0 x i32], align 4
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-extractvalue.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-extractvalue.ll
index b1e7968c7603d..d1fd05aad7dc4 100644
--- a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-extractvalue.ll
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-extractvalue.ll
@@ -1,8 +1,9 @@
 ; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
 
-; Test that extractvalue of zero-size array is replaced with poison
+; Test that extractvalue of zero-size array is replaced with poison.
 
-; Can't run spirv-val as function signatures aren't handled
+; Can't run spirv-val as function signatures are not handled.
+; RUNx: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
 define [0 x i32] @test_extractvalue_zero_array() {
 ; CHECK-LABEL: @test_extractvalue_zero_array(
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-global.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-global.ll
index e90478d4c86d6..00438d33b8c2a 100644
--- a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-global.ll
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-global.ll
@@ -1,8 +1,8 @@
 ; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
-; Test that a global variable with zero-size array is transformed to ptr type
+; Test that a global variable with zero-size array is transformed to ptr type.
 
 @global_zero_array = global [0 x i32] zeroinitializer
 
-; CHECK: @global_zero_array = global ptr null
+; CHECK: @global_zero_array = global ptr addrspace(4) null
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-insertvalue.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-insertvalue.ll
index b20b84190b092..90e3a317750ea 100644
--- a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-insertvalue.ll
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-insertvalue.ll
@@ -1,6 +1,9 @@
 ; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
 
-; Test that insertvalue of zero-size array is removed
+; Test that insertvalue of zero-size array is removed.
+
+; Can't run spirv-val as function signatures are not handled.
+; RUNx: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
 %struct.with_zero = type { i32, [0 x i32], i64 }
 
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-load.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-load.ll
index 4209fc27d579d..2aa2c7e7fc43c 100644
--- a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-load.ll
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-load.ll
@@ -1,7 +1,7 @@
 ; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
-; Test that load of zero-size array is replaced with poison and removed
+; Test that load of zero-size array is replaced with poison and removed.
 
 define void @test_load_zero_array(ptr %ptr) {
 ; CHECK-LABEL: @test_load_zero_array(
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-nested.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-nested.ll
index 45b24998321ce..b522ebea6cd27 100644
--- a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-nested.ll
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-nested.ll
@@ -1,8 +1,8 @@
 ; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
-; Test that nested zero-size arrays are legalized to pointers
+; Test that nested zero-size arrays are legalized to pointers.
 
 @nested_zero_array = global [2 x [0 x i32]] zeroinitializer
 
-; CHECK: @nested_zero_array = global [2 x ptr] zeroinitializer
+; CHECK: @nested_zero_array = global [2 x ptr addrspace(4)] zeroinitializer
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-select.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-select.ll
index 45cb959058078..e458e4bbe7a30 100644
--- a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-select.ll
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-select.ll
@@ -1,8 +1,9 @@
 ; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
 
-; Test that select of zero-size array is replaced with poison
+; Test that select of zero-size array is replaced with poison.
 
-; Can't run spirv-val as function signatures are not handled
+; Can't run spirv-val as function signatures are not handled.
+; RUNx: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
 define [0 x i32] @test_select_zero_array(i1 %cond, [0 x i32] %a) {
 ; CHECK-LABEL: @test_select_zero_array(
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-store.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-store.ll
index 5fe1ef8affaa7..2579219d0acb1 100644
--- a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-store.ll
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-store.ll
@@ -1,7 +1,7 @@
 ; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
-; Test that store of zero-size array is removed
+; Test that store of zero-size array is removed.
 
 define void @test_store_zero_array(ptr %ptr) {
 ; CHECK-LABEL: @test_store_zero_array(
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-struct.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-struct.ll
index e2210a772c4fd..f7b93cdbc8b74 100644
--- a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-struct.ll
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-struct.ll
@@ -1,11 +1,11 @@
 ; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
-; Test that struct with zero-size array field becomes pointer
+; Test that struct with zero-size array field becomes pointer.
 
 %struct.with_zero = type { i32, [0 x i32], i32 }
 
 @global_struct = global %struct.with_zero zeroinitializer
 
-; CHECK: %struct.with_zero.legalized = type { i32, ptr, i32 }
+; CHECK: %struct.with_zero.legalized = type { i32, ptr addrspace(4), i32 }
 ; CHECK: @global_struct = global %struct.with_zero.legalized zeroinitializer
diff --git a/llvm/test/CodeGen/SPIRV/zero-length-array.ll b/llvm/test/CodeGen/SPIRV/zero-length-array.ll
index 70936e0515b2b..0a13a90e5d323 100644
--- a/llvm/test/CodeGen/SPIRV/zero-length-array.ll
+++ b/llvm/test/CodeGen/SPIRV/zero-length-array.ll
@@ -17,8 +17,10 @@
 ; For non-compute, error.
 ; CHECK-ERR: LLVM ERROR: Runtime arrays are not allowed in non-shader SPIR-V modules
 
+%struct.with_zero = type { i32, [0 x i32], i64 }
+
 define spir_func void @foo() {
 entry:
-  %i = alloca [0 x [ 0 x i32]], align 4
+  %i = alloca %struct.with_zero, align 64
   ret void
 }

>From f5c57087286cc9950950d361c841a33f2f82eafc Mon Sep 17 00:00:00 2001
From: Nick Sarnie <nick.sarnie at intel.com>
Date: Tue, 16 Dec 2025 12:52:44 -0800
Subject: [PATCH 3/4] add comment

Signed-off-by: Nick Sarnie <nick.sarnie at intel.com>
---
 llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
index 41ef344903ddb..886ff89b54057 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
@@ -194,6 +194,7 @@ void SPIRVLegalizeZeroSizeArraysImpl::visitAllocaInst(AllocaInst &AI) {
   // TODO: Handle structs containing zero-size arrays.
   ArrayType *ArrTy = dyn_cast<ArrayType>(AI.getAllocatedType());
   if (shouldLegalizeInstType(ArrTy)) {
+    // Allocate a generic pointer instead of an empty array.
     IRBuilder<> Builder(&AI);
     AllocaInst *NewAI = Builder.CreateAlloca(
         PointerType::get(

>From 31f0190ef8e66d56a11097a1be044fa6454f619b Mon Sep 17 00:00:00 2001
From: Nick Sarnie <nick.sarnie at intel.com>
Date: Wed, 17 Dec 2025 08:45:03 -0800
Subject: [PATCH 4/4] address feedback 2

Signed-off-by: Nick Sarnie <nick.sarnie at intel.com>
---
 .../SPIRV/SPIRVLegalizeZeroSizeArrays.cpp     | 51 ++++++++++++-------
 .../SPIRV/legalize-zero-size-arrays-undef.ll  |  8 +++
 2 files changed, 42 insertions(+), 17 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-undef.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
index 886ff89b54057..d9bf3fbf15d28 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
@@ -47,6 +47,8 @@ bool hasZeroSizeArray(const Type *Ty) {
 }
 
 bool shouldLegalizeInstType(const Type *Ty) {
+  // This recursive function will always terminate because we only look inside
+  // array types, and those can't be recursive.
   if (const ArrayType *ArrTy = dyn_cast_if_present<ArrayType>(Ty)) {
     return ArrTy->getNumElements() == 0 ||
            shouldLegalizeInstType(ArrTy->getElementType());
@@ -99,6 +101,25 @@ class SPIRVLegalizeZeroSizeArraysLegacy : public ModulePass {
   const SPIRVTargetMachine *TM;
 };
 
+// clang-format off
+
+// Legalize a type. There are only two cases we need to care about:
+// arrays and structs.
+//
+// For arrays, if it is not a nested array ([0 x i32] or if it is nested with a
+// zero element count ([0 x [0 x i32]]), we just replace the entire array type
+// with a ptr.
+//
+// If it is nested with a non-zero element count( [5 x [0 x i32]]),
+// we replace the element type with a legalized element type and maintain the
+// existing element count.
+// For the example type we would replace with [5 x ptr]).
+//
+// For structs, we create a new type with any members containing nested
+// arrays legalized.
+
+// clang-format on
+
 Type *SPIRVLegalizeZeroSizeArraysImpl::legalizeType(Type *Ty) {
   auto It = TypeMap.find(Ty);
   if (It != TypeMap.end())
@@ -111,9 +132,11 @@ Type *SPIRVLegalizeZeroSizeArraysImpl::legalizeType(Type *Ty) {
       LegalizedTy = PointerType::get(
           Ty->getContext(),
           storageClassToAddressSpace(SPIRV::StorageClass::Generic));
-    } else if (Type *ElemTy = legalizeType(ArrTy->getElementType());
-               ElemTy != ArrTy->getElementType()) {
-      LegalizedTy = ArrayType::get(ElemTy, ArrTy->getNumElements());
+    } else {
+      Type *ElemTy = ArrTy->getElementType();
+      Type *LegalElemTy = legalizeType(ElemTy);
+      if (LegalElemTy != ElemTy)
+        LegalizedTy = ArrayType::get(LegalElemTy, ArrTy->getNumElements());
     }
   } else if (StructType *StructTy = dyn_cast<StructType>(Ty)) {
     SmallVector<Type *, 8> ElemTypes;
@@ -153,7 +176,6 @@ Constant *SPIRVLegalizeZeroSizeArraysImpl::legalizeConstant(Constant *C) {
     return PoisonValue::get(NewTy);
   if (isa<ConstantAggregateZero>(C))
     return Constant::getNullValue(NewTy);
-
   if (ConstantArray *CA = dyn_cast<ConstantArray>(C)) {
     SmallVector<Constant *, 8> Elems;
     for (Use &U : CA->operands())
@@ -285,8 +307,8 @@ bool SPIRVLegalizeZeroSizeArraysImpl::runOnModule(Module &M) {
   if (TM && TM->getSubtargetImpl()->isShader())
     return false;
 
-  // First pass: create new globals and track mapping (don't erase old ones
-  // yet).
+  // First pass: create new globals (legalizing the initializer as needed) and
+  // track mapping (don't erase old ones yet).
   SmallVector<GlobalVariable *, 8> OldGlobals;
   for (GlobalVariable &GV : M.globals()) {
     if (!hasZeroSizeArray(GV.getValueType()))
@@ -297,10 +319,12 @@ bool SPIRVLegalizeZeroSizeArraysImpl::runOnModule(Module &M) {
       continue;
 
     Type *NewTy = legalizeType(GV.getValueType());
-    // Use an empty name and initializer for now, we will update them in the
-    // following steps.
+    Constant *LegalizedInitializer = legalizeConstant(GV.getInitializer());
+
+    // Use an empty name for now, we will update it in the
+    // following step.
     GlobalVariable *NewGV = new GlobalVariable(
-        M, NewTy, GV.isConstant(), GV.getLinkage(), /*Initializer=*/nullptr,
+        M, NewTy, GV.isConstant(), GV.getLinkage(), LegalizedInitializer,
         /*Name=*/"", &GV, GV.getThreadLocalMode(), GV.getAddressSpace(),
         GV.isExternallyInitialized());
     NewGV->copyAttributesFrom(&GV);
@@ -312,14 +336,7 @@ bool SPIRVLegalizeZeroSizeArraysImpl::runOnModule(Module &M) {
     Modified = true;
   }
 
-  // Second pass: set initializers now that all globals are mapped.
-  for (GlobalVariable *GV : OldGlobals) {
-    GlobalVariable *NewGV = cast<GlobalVariable>(GlobalMap[GV]);
-    if (GV->hasInitializer())
-      NewGV->setInitializer(legalizeConstant(GV->getInitializer()));
-  }
-
-  // Third pass: replace uses, transfer names, and erase old globals.
+  // Second pass: replace uses, transfer names, and erase old globals.
   for (GlobalVariable *GV : OldGlobals) {
     GlobalVariable *NewGV = GlobalMap[GV];
     GV->replaceAllUsesWith(ConstantExpr::getBitCast(NewGV, GV->getType()));
diff --git a/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-undef.ll b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-undef.ll
new file mode 100644
index 0000000000000..d38ee8ea8e5a9
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-undef.ll
@@ -0,0 +1,8 @@
+; RUN: opt -S -passes=spirv-legalize-zero-size-arrays -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Test that poision initializers are legalized correctly.
+
+ at nested_zero_array = global [2 x [0 x i32]] poison
+
+; CHECK: @nested_zero_array = global [2 x ptr addrspace(4)] poison



More information about the llvm-commits mailing list