[SPIRV] Add legalization pass for zero-size arrays (#172367)
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]` with `ptr`. For instructions, we either replace `[0 x T]` with `poision`, for `alloca` we just allocate `T`. This is motivated by IR generated by the OpenMP front end. Issue: https://github.com/llvm/llvm-project/issues/170150 --------- Signed-off-by: Nick Sarnie <nick.sarnie@intel.com>
This commit is contained in:
parent
88788dcd5b
commit
75ec177483
@ -27,6 +27,7 @@ add_llvm_target(SPIRVCodeGen
|
||||
SPIRVInstrInfo.cpp
|
||||
SPIRVInstructionSelector.cpp
|
||||
SPIRVLegalizeImplicitBinding.cpp
|
||||
SPIRVLegalizeZeroSizeArrays.cpp
|
||||
SPIRVStripConvergentIntrinsics.cpp
|
||||
SPIRVLegalizePointerCast.cpp
|
||||
SPIRVMergeRegionExitTargets.cpp
|
||||
|
||||
@ -26,6 +26,7 @@ ModulePass *createSPIRVPushConstantAccessLegacyPass(SPIRVTargetMachine *TM);
|
||||
FunctionPass *createSPIRVMergeRegionExitTargetsPass();
|
||||
FunctionPass *createSPIRVStripConvergenceIntrinsicsPass();
|
||||
ModulePass *createSPIRVLegalizeImplicitBindingPass();
|
||||
ModulePass *createSPIRVLegalizeZeroSizeArraysPass(const SPIRVTargetMachine &TM);
|
||||
FunctionPass *createSPIRVLegalizePointerCastPass(SPIRVTargetMachine *TM);
|
||||
FunctionPass *createSPIRVRegularizerPass();
|
||||
FunctionPass *createSPIRVPreLegalizerCombiner();
|
||||
@ -57,6 +58,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
|
||||
|
||||
359
llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
Normal file
359
llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.cpp
Normal file
@ -0,0 +1,359 @@
|
||||
//===- 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 "SPIRVTargetMachine.h"
|
||||
#include "SPIRVUtils.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"
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
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.
|
||||
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);
|
||||
|
||||
const SPIRVTargetMachine &TM;
|
||||
DenseMap<Type *, Type *> TypeMap;
|
||||
DenseMap<GlobalVariable *, GlobalVariable *> GlobalMap;
|
||||
SmallVector<Instruction *, 16> ToErase;
|
||||
bool Modified = false;
|
||||
};
|
||||
|
||||
class SPIRVLegalizeZeroSizeArraysLegacy : public ModulePass {
|
||||
public:
|
||||
static char 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(TM);
|
||||
return Impl.runOnModule(M);
|
||||
}
|
||||
|
||||
private:
|
||||
const SPIRVTargetMachine &TM;
|
||||
};
|
||||
|
||||
// Legalize a type. There are only two cases we need to care about:
|
||||
// arrays and structs.
|
||||
//
|
||||
// For arrays, we just replace the entire array type with a ptr.
|
||||
//
|
||||
// For structs, we create a new type with any members containing
|
||||
// nested arrays legalized.
|
||||
|
||||
Type *SPIRVLegalizeZeroSizeArraysImpl::legalizeType(Type *Ty) {
|
||||
auto It = TypeMap.find(Ty);
|
||||
if (It != TypeMap.end())
|
||||
return It->second;
|
||||
|
||||
Type *LegalizedTy = Ty;
|
||||
|
||||
if (isa<ArrayType>(Ty)) {
|
||||
LegalizedTy = PointerType::get(
|
||||
Ty->getContext(),
|
||||
storageClassToAddressSpace(SPIRV::StorageClass::Generic));
|
||||
|
||||
} 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)) {
|
||||
if (GlobalVariable *NewGV = GlobalMap.lookup(GV))
|
||||
return NewGV;
|
||||
return C;
|
||||
}
|
||||
|
||||
Type *NewTy = legalizeType(C->getType());
|
||||
if (isa<UndefValue>(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 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(
|
||||
ArrTy->getContext(),
|
||||
storageClassToAddressSpace(SPIRV::StorageClass::Generic)),
|
||||
/*ArraySize=*/nullptr, 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 structs containing zero-size arrays.
|
||||
ArrayType *ArrTy = dyn_cast<ArrayType>(LI.getType());
|
||||
if (shouldLegalizeInstType(ArrTy)) {
|
||||
LI.replaceAllUsesWith(PoisonValue::get(LI.getType()));
|
||||
ToErase.push_back(&LI);
|
||||
Modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SPIRVLegalizeZeroSizeArraysImpl::visitStoreInst(StoreInst &SI) {
|
||||
Type *StoreTy = SI.getValueOperand()->getType();
|
||||
|
||||
// TODO: Handle structs containing zero-size arrays.
|
||||
ArrayType *ArrTy = dyn_cast<ArrayType>(StoreTy);
|
||||
if (shouldLegalizeInstType(ArrTy)) {
|
||||
ToErase.push_back(&SI);
|
||||
Modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SPIRVLegalizeZeroSizeArraysImpl::visitSelectInst(SelectInst &Sel) {
|
||||
if (!hasZeroSizeArray(Sel.getType()))
|
||||
return;
|
||||
|
||||
// TODO: Handle structs containing zero-size arrays.
|
||||
ArrayType *ArrTy = dyn_cast<ArrayType>(Sel.getType());
|
||||
if (shouldLegalizeInstType(ArrTy)) {
|
||||
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 structs containing zero-size arrays.
|
||||
ArrayType *ArrTy = dyn_cast<ArrayType>(EVI.getType());
|
||||
if (shouldLegalizeInstType(ArrTy)) {
|
||||
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 structs containing zero-size arrays.
|
||||
ArrayType *ArrTy =
|
||||
dyn_cast<ArrayType>(IVI.getInsertedValueOperand()->getType());
|
||||
if (shouldLegalizeInstType(ArrTy)) {
|
||||
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.
|
||||
if (TM.getSubtargetImpl()->isShader())
|
||||
return false;
|
||||
|
||||
// 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()))
|
||||
continue;
|
||||
|
||||
// llvm.embedded.module is handled by SPIRVPrepareGlobals.
|
||||
if (GV.getName() == "llvm.embedded.module")
|
||||
continue;
|
||||
|
||||
Type *NewTy = legalizeType(GV.getValueType());
|
||||
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(), LegalizedInitializer,
|
||||
/*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: 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(TM);
|
||||
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(const SPIRVTargetMachine &TM) {
|
||||
return new SPIRVLegalizeZeroSizeArraysLegacy(TM);
|
||||
}
|
||||
29
llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.h
Normal file
29
llvm/lib/Target/SPIRV/SPIRVLegalizeZeroSizeArrays.h
Normal file
@ -0,0 +1,29 @@
|
||||
//===- 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 SPIRVTargetMachine;
|
||||
|
||||
class SPIRVLegalizeZeroSizeArrays
|
||||
: public PassInfoMixin<SPIRVLegalizeZeroSizeArrays> {
|
||||
const SPIRVTargetMachine &TM;
|
||||
|
||||
public:
|
||||
SPIRVLegalizeZeroSizeArrays(const SPIRVTargetMachine &TM) : TM(TM) {}
|
||||
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_LIB_TARGET_SPIRV_SPIRVLEGALIZEZEROSIZE_ARRAYS_H_
|
||||
@ -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(*static_cast<const SPIRVTargetMachine *>(this)))
|
||||
MODULE_PASS("spirv-pushconstant-access", SPIRVPushConstantAccess(*static_cast<const SPIRVTargetMachine *>(this)))
|
||||
#undef MODULE_PASS
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#include "SPIRV.h"
|
||||
#include "SPIRVCBufferAccess.h"
|
||||
#include "SPIRVGlobalRegistry.h"
|
||||
#include "SPIRVLegalizeZeroSizeArrays.h"
|
||||
#include "SPIRVLegalizerInfo.h"
|
||||
#include "SPIRVPushConstantAccess.h"
|
||||
#include "SPIRVStructurizerWrapper.h"
|
||||
@ -54,6 +55,7 @@ extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() {
|
||||
initializeSPIRVPushConstantAccessLegacyPass(PR);
|
||||
initializeSPIRVPreLegalizerCombinerPass(PR);
|
||||
initializeSPIRVLegalizePointerCastPass(PR);
|
||||
initializeSPIRVLegalizeZeroSizeArraysLegacyPass(PR);
|
||||
initializeSPIRVRegularizerPass(PR);
|
||||
initializeSPIRVPreLegalizerPass(PR);
|
||||
initializeSPIRVPostLegalizerPass(PR);
|
||||
@ -209,15 +211,15 @@ void SPIRVPassConfig::addISelPrepare() {
|
||||
// back to virtual registers.
|
||||
addPass(createPromoteMemoryToRegisterPass());
|
||||
}
|
||||
|
||||
SPIRVTargetMachine &TM = getTM<SPIRVTargetMachine>();
|
||||
addPass(createSPIRVStripConvergenceIntrinsicsPass());
|
||||
addPass(createSPIRVLegalizeImplicitBindingPass());
|
||||
addPass(createSPIRVLegalizeZeroSizeArraysPass(TM));
|
||||
addPass(createSPIRVCBufferAccessLegacyPass());
|
||||
addPass(
|
||||
createSPIRVPushConstantAccessLegacyPass(&getTM<SPIRVTargetMachine>()));
|
||||
addPass(createSPIRVEmitIntrinsicsPass(&getTM<SPIRVTargetMachine>()));
|
||||
addPass(createSPIRVPushConstantAccessLegacyPass(&TM));
|
||||
addPass(createSPIRVEmitIntrinsicsPass(&TM));
|
||||
if (TM.getSubtargetImpl()->isLogicalSPIRV())
|
||||
addPass(createSPIRVLegalizePointerCastPass(&getTM<SPIRVTargetMachine>()));
|
||||
addPass(createSPIRVLegalizePointerCastPass(&TM));
|
||||
TargetPassConfig::addISelPrepare();
|
||||
}
|
||||
|
||||
|
||||
@ -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 ptr addrspace(4), align 4
|
||||
; CHECK-NEXT: ret void
|
||||
%arr = alloca [0 x i32], i32 %n, align 4
|
||||
ret void
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
14
llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca.ll
Normal file
14
llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-alloca.ll
Normal file
@ -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 ptr addrspace(4), align 4
|
||||
; CHECK-NEXT: ret void
|
||||
entry:
|
||||
%arr = alloca [0 x i32], align 4
|
||||
ret void
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
; 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 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(
|
||||
; CHECK-NEXT: ret [0 x i32] poison
|
||||
%arr = extractvalue [1 x [0 x i32]] zeroinitializer, 0
|
||||
ret [0 x i32] %arr
|
||||
}
|
||||
@ -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 addrspace(4) null
|
||||
@ -0,0 +1,19 @@
|
||||
; 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.
|
||||
|
||||
; 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 }
|
||||
|
||||
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
|
||||
}
|
||||
13
llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-load.ll
Normal file
13
llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-load.ll
Normal file
@ -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
|
||||
}
|
||||
@ -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 a pointer.
|
||||
|
||||
@nested_zero_array = global [2 x [0 x i32]] zeroinitializer
|
||||
|
||||
; CHECK: @nested_zero_array = global ptr addrspace(4) null
|
||||
13
llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-select.ll
Normal file
13
llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-select.ll
Normal file
@ -0,0 +1,13 @@
|
||||
; 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.
|
||||
; 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(
|
||||
; CHECK-NEXT: ret [0 x i32] poison
|
||||
%result = select i1 %cond, [0 x i32] zeroinitializer, [0 x i32] %a
|
||||
ret [0 x i32] %result
|
||||
}
|
||||
13
llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-store.ll
Normal file
13
llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-store.ll
Normal file
@ -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
|
||||
}
|
||||
11
llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-struct.ll
Normal file
11
llvm/test/CodeGen/SPIRV/legalize-zero-size-arrays-struct.ll
Normal file
@ -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 }
|
||||
|
||||
@global_struct = global %struct.with_zero zeroinitializer
|
||||
|
||||
; CHECK: %struct.with_zero.legalized = type { i32, ptr addrspace(4), i32 }
|
||||
; CHECK: @global_struct = global %struct.with_zero.legalized zeroinitializer
|
||||
@ -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 to a pointer.
|
||||
|
||||
@nested_zero_array = global [2 x [0 x i32]] poison
|
||||
|
||||
; CHECK: @nested_zero_array = global ptr addrspace(4) poison
|
||||
@ -38,6 +38,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 push constant Access
|
||||
; SPIRV-O0-NEXT: SPIRV emit intrinsics
|
||||
@ -143,6 +144,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 push constant Access
|
||||
; SPIRV-Opt-NEXT: SPIRV emit intrinsics
|
||||
|
||||
@ -14,8 +14,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 i32], align 4
|
||||
%i = alloca %struct.with_zero, align 64
|
||||
ret void
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user