-
Notifications
You must be signed in to change notification settings - Fork 15.5k
[SPIRV] Add legalization pass for zero-size arrays #172367
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
✅ With the latest revision this PR passed the undef deprecator. |
Signed-off-by: Nick Sarnie <nick.sarnie@intel.com>
|
@llvm/pr-subscribers-backend-spir-v Author: Nick Sarnie (sarnex) ChangesThis adds a legalization pass to convert zero size arrays to legal types for common cases. It doesn't handle all cases, but if we see real use cases for it, we can add them in the future. For globals, and their initializers, we generally replace For instructions, we either replace This is motivated by IR generated by the OpenMP front end. Issue: #170150 Patch is 24.65 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/172367.diff 18 Files Affected:
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
+
+@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-unk...
[truncated]
|
|
Hello, Could you share a simple source code and compile command to play with? |
jmmartinez
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some minor comments. I'll do a deeper read later.
| DenseMap<Type *, Type *> TypeMap; | ||
| DenseMap<GlobalVariable *, GlobalVariable *> GlobalMap; | ||
| SmallVector<Instruction *, 16> ToErase; | ||
| bool Modified = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't fully checked the code but I think !ToErase.empty() == Modified
|
|
||
| if (ArrayType *ArrTy = dyn_cast<ArrayType>(Ty)) { | ||
| if (ArrTy->getNumElements() == 0) { | ||
| LegalizedTy = PointerType::getUnqual(Ty->getContext()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unqual gives you a pointer to function in SPIRV. I think that you'd rather want a pointer to generic (AS 4)
| if (GlobalVariable *GV = dyn_cast<GlobalVariable>(C)) | ||
| return GlobalMap.lookup(GV) ? GlobalMap[GV] : C; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid doing 2 lookups.
| 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; | |
| } |
| return GlobalMap.lookup(GV) ? GlobalMap[GV] : C; | ||
|
|
||
| Type *NewTy = legalizeType(C->getType()); | ||
| if (isa<UndefValue>(C) || isa<PoisonValue>(C)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PoisonValue is an instance of UndefValue.
| if (isa<UndefValue>(C) || isa<PoisonValue>(C)) | |
| if (isa<UndefValue>(C)) |
| bool Modified = false; | ||
| }; | ||
|
|
||
| class SPIRVLegalizeZeroSizeArraysLegacy : public ModulePass { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why Legacy?
|
|
||
| // TODO: Handle nested arrays and structs containing zero-size arrays | ||
| ArrayType *ArrTy = dyn_cast<ArrayType>(AI.getAllocatedType()); | ||
| if (ArrTy && ArrTy->getNumElements() == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this check redundant with the early return?
| Modified = false; | ||
|
|
||
| // Runtime arrays are allowed for shaders, so we don't need to do anything. | ||
| Triple Triple(M.getTargetTriple()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if (!hasZeroSizeArray(GV.getValueType())) | ||
| continue; | ||
|
|
||
| // llvm.embedded.module is handled by SPIRVPrepareGlobals |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // llvm.embedded.module is handled by SPIRVPrepareGlobals | |
| // llvm.embedded.module is handled by SPIRVPrepareGlobals. |
| ; 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| ; 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. |
| ; 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| ; Test that nested zero-size arrays are legalized to pointers | |
| ; Test that nested zero-size arrays are legalized to pointers. |
| @@ -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 | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| ; Test that select of zero-size array is replaced with poison | |
| ; 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, add the line as RUNx or at least TODO.
| ; 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| ; Test that store of zero-size array is removed | |
| ; Test that store of zero-size array is removed. |
| ; 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| ; Test that struct with zero-size array field becomes pointer | |
| ; Test that struct with zero-size array field becomes pointer. |
This adds a legalization pass to convert zero size arrays to legal types for common cases. It doesn't handle all cases, but if we see real use cases for other cases, we can add them in the future.
For globals, and their initializers, we generally replace
[0 x T]withptr.For instructions, we either replace
[0 x T]withpoision, forallocawe just allocateT.This is motivated by IR generated by the OpenMP front end.
Issue: #170150