[llvm] [SPIRV] Add legalization pass for zero-size arrays (PR #172367)
Nick Sarnie via llvm-commits
llvm-commits at lists.llvm.org
Mon Dec 15 13:48:11 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] [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
}
More information about the llvm-commits
mailing list