LLVM 22.0.0git
SPIRVLegalizeZeroSizeArrays.cpp
Go to the documentation of this file.
1//===- SPIRVLegalizeZeroSizeArrays.cpp - Legalize zero-size arrays -------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// SPIR-V does not support zero-size arrays unless it is within a shader. This
10// pass legalizes zero-size arrays ([0 x T]) in unsupported cases.
11//
12//===----------------------------------------------------------------------===//
13
15#include "SPIRV.h"
16#include "SPIRVTargetMachine.h"
17#include "SPIRVUtils.h"
18#include "llvm/ADT/DenseMap.h"
20#include "llvm/IR/IRBuilder.h"
22#include "llvm/IR/InstVisitor.h"
23#include "llvm/Pass.h"
24#include "llvm/Support/Debug.h"
25
26#define DEBUG_TYPE "spirv-legalize-zero-size-arrays"
27
28using namespace llvm;
29
30namespace {
31
32bool hasZeroSizeArray(const Type *Ty) {
33 if (const ArrayType *ArrTy = dyn_cast<ArrayType>(Ty)) {
34 if (ArrTy->getNumElements() == 0)
35 return true;
36 return hasZeroSizeArray(ArrTy->getElementType());
37 }
38
39 if (const StructType *StructTy = dyn_cast<StructType>(Ty)) {
40 for (Type *ElemTy : StructTy->elements()) {
41 if (hasZeroSizeArray(ElemTy))
42 return true;
43 }
44 }
45
46 return false;
47}
48
49bool shouldLegalizeInstType(const Type *Ty) {
50 // This recursive function will always terminate because we only look inside
51 // array types, and those can't be recursive.
52 if (const ArrayType *ArrTy = dyn_cast_if_present<ArrayType>(Ty)) {
53 return ArrTy->getNumElements() == 0 ||
54 shouldLegalizeInstType(ArrTy->getElementType());
55 }
56 return false;
57}
58
59class SPIRVLegalizeZeroSizeArraysImpl
60 : public InstVisitor<SPIRVLegalizeZeroSizeArraysImpl> {
61 friend class InstVisitor<SPIRVLegalizeZeroSizeArraysImpl>;
62
63public:
64 SPIRVLegalizeZeroSizeArraysImpl(const SPIRVTargetMachine &TM)
65 : InstVisitor(), TM(TM) {}
66 bool runOnModule(Module &M);
67
68 // TODO: Handle GEP, PHI.
69 void visitAllocaInst(AllocaInst &AI);
70 void visitLoadInst(LoadInst &LI);
71 void visitStoreInst(StoreInst &SI);
72 void visitSelectInst(SelectInst &Sel);
73 void visitExtractValueInst(ExtractValueInst &EVI);
74 void visitInsertValueInst(InsertValueInst &IVI);
75
76private:
77 Type *legalizeType(Type *Ty);
78 Constant *legalizeConstant(Constant *C);
79
80 const SPIRVTargetMachine &TM;
84 bool Modified = false;
85};
86
87class SPIRVLegalizeZeroSizeArraysLegacy : public ModulePass {
88public:
89 static char ID;
90 SPIRVLegalizeZeroSizeArraysLegacy(const SPIRVTargetMachine &TM)
91 : ModulePass(ID), TM(TM) {}
92 StringRef getPassName() const override {
93 return "SPIRV Legalize Zero-Size Arrays";
94 }
95 bool runOnModule(Module &M) override {
96 SPIRVLegalizeZeroSizeArraysImpl Impl(TM);
97 return Impl.runOnModule(M);
98 }
99
100private:
101 const SPIRVTargetMachine &TM;
102};
103
104// Legalize a type. There are only two cases we need to care about:
105// arrays and structs.
106//
107// For arrays, we just replace the entire array type with a ptr.
108//
109// For structs, we create a new type with any members containing
110// nested arrays legalized.
111
112Type *SPIRVLegalizeZeroSizeArraysImpl::legalizeType(Type *Ty) {
113 auto It = TypeMap.find(Ty);
114 if (It != TypeMap.end())
115 return It->second;
116
117 Type *LegalizedTy = Ty;
118
119 if (isa<ArrayType>(Ty)) {
120 LegalizedTy = PointerType::get(
121 Ty->getContext(),
122 storageClassToAddressSpace(SPIRV::StorageClass::Generic));
123
124 } else if (StructType *StructTy = dyn_cast<StructType>(Ty)) {
125 SmallVector<Type *, 8> ElemTypes;
126 bool Changed = false;
127 for (Type *ElemTy : StructTy->elements()) {
128 Type *LegalizedElemTy = legalizeType(ElemTy);
129 ElemTypes.push_back(LegalizedElemTy);
130 Changed |= LegalizedElemTy != ElemTy;
131 }
132 if (Changed) {
133 LegalizedTy =
134 StructTy->hasName()
135 ? StructType::create(StructTy->getContext(), ElemTypes,
136 (StructTy->getName() + ".legalized").str(),
137 StructTy->isPacked())
138 : StructType::get(StructTy->getContext(), ElemTypes,
139 StructTy->isPacked());
140 }
141 }
142
143 TypeMap[Ty] = LegalizedTy;
144 return LegalizedTy;
145}
146
147Constant *SPIRVLegalizeZeroSizeArraysImpl::legalizeConstant(Constant *C) {
148 if (!C || !hasZeroSizeArray(C->getType()))
149 return C;
150
152 if (GlobalVariable *NewGV = GlobalMap.lookup(GV))
153 return NewGV;
154 return C;
155 }
156
157 Type *NewTy = legalizeType(C->getType());
158 if (isa<UndefValue>(C))
159 return PoisonValue::get(NewTy);
161 return Constant::getNullValue(NewTy);
164 for (Use &U : CA->operands())
165 Elems.push_back(legalizeConstant(cast<Constant>(U)));
166 return ConstantArray::get(cast<ArrayType>(NewTy), Elems);
167 }
168
171 for (Use &U : CS->operands())
172 Fields.push_back(legalizeConstant(cast<Constant>(U)));
173 return ConstantStruct::get(cast<StructType>(NewTy), Fields);
174 }
175
177 // Don't legalize GEP constant expressions, the backend deals with them
178 // fine.
179 if (CE->getOpcode() == Instruction::GetElementPtr)
180 return CE;
182 bool Changed = false;
183 for (Use &U : CE->operands()) {
184 Constant *LegalizedOp = legalizeConstant(cast<Constant>(U));
185 Ops.push_back(LegalizedOp);
186 Changed |= LegalizedOp != cast<Constant>(U.get());
187 }
188 if (Changed)
189 return CE->getWithOperands(Ops);
190 }
191
192 return C;
193}
194
195void SPIRVLegalizeZeroSizeArraysImpl::visitAllocaInst(AllocaInst &AI) {
196 if (!hasZeroSizeArray(AI.getAllocatedType()))
197 return;
198
199 // TODO: Handle structs containing zero-size arrays.
201 if (shouldLegalizeInstType(ArrTy)) {
202 // Allocate a generic pointer instead of an empty array.
203 IRBuilder<> Builder(&AI);
204 AllocaInst *NewAI = Builder.CreateAlloca(
206 ArrTy->getContext(),
207 storageClassToAddressSpace(SPIRV::StorageClass::Generic)),
208 /*ArraySize=*/nullptr, AI.getName());
209 NewAI->setAlignment(AI.getAlign());
210 NewAI->setDebugLoc(AI.getDebugLoc());
211 AI.replaceAllUsesWith(NewAI);
212 ToErase.push_back(&AI);
213 Modified = true;
214 }
215}
216
217void SPIRVLegalizeZeroSizeArraysImpl::visitLoadInst(LoadInst &LI) {
218 if (!hasZeroSizeArray(LI.getType()))
219 return;
220
221 // TODO: Handle structs containing zero-size arrays.
223 if (shouldLegalizeInstType(ArrTy)) {
225 ToErase.push_back(&LI);
226 Modified = true;
227 }
228}
229
230void SPIRVLegalizeZeroSizeArraysImpl::visitStoreInst(StoreInst &SI) {
231 Type *StoreTy = SI.getValueOperand()->getType();
232
233 // TODO: Handle structs containing zero-size arrays.
234 ArrayType *ArrTy = dyn_cast<ArrayType>(StoreTy);
235 if (shouldLegalizeInstType(ArrTy)) {
236 ToErase.push_back(&SI);
237 Modified = true;
238 }
239}
240
241void SPIRVLegalizeZeroSizeArraysImpl::visitSelectInst(SelectInst &Sel) {
242 if (!hasZeroSizeArray(Sel.getType()))
243 return;
244
245 // TODO: Handle structs containing zero-size arrays.
246 ArrayType *ArrTy = dyn_cast<ArrayType>(Sel.getType());
247 if (shouldLegalizeInstType(ArrTy)) {
249 ToErase.push_back(&Sel);
250 Modified = true;
251 }
252}
253
254void SPIRVLegalizeZeroSizeArraysImpl::visitExtractValueInst(
255 ExtractValueInst &EVI) {
256 if (!hasZeroSizeArray(EVI.getAggregateOperand()->getType()))
257 return;
258
259 // TODO: Handle structs containing zero-size arrays.
260 ArrayType *ArrTy = dyn_cast<ArrayType>(EVI.getType());
261 if (shouldLegalizeInstType(ArrTy)) {
263 ToErase.push_back(&EVI);
264 Modified = true;
265 }
266}
267
268void SPIRVLegalizeZeroSizeArraysImpl::visitInsertValueInst(
269 InsertValueInst &IVI) {
270 if (!hasZeroSizeArray(IVI.getAggregateOperand()->getType()))
271 return;
272
273 // TODO: Handle structs containing zero-size arrays.
274 ArrayType *ArrTy =
276 if (shouldLegalizeInstType(ArrTy)) {
278 ToErase.push_back(&IVI);
279 Modified = true;
280 }
281}
282
283bool SPIRVLegalizeZeroSizeArraysImpl::runOnModule(Module &M) {
284 TypeMap.clear();
285 GlobalMap.clear();
286 ToErase.clear();
287 Modified = false;
288
289 // Runtime arrays are allowed for shaders, so we don't need to do anything.
290 if (TM.getSubtargetImpl()->isShader())
291 return false;
292
293 // First pass: create new globals (legalizing the initializer as needed) and
294 // track mapping (don't erase old ones yet).
296 for (GlobalVariable &GV : M.globals()) {
297 if (!hasZeroSizeArray(GV.getValueType()))
298 continue;
299
300 // llvm.embedded.module is handled by SPIRVPrepareGlobals.
301 if (GV.getName() == "llvm.embedded.module")
302 continue;
303
304 Type *NewTy = legalizeType(GV.getValueType());
305 Constant *LegalizedInitializer = legalizeConstant(GV.getInitializer());
306
307 // Use an empty name for now, we will update it in the
308 // following step.
309 GlobalVariable *NewGV = new GlobalVariable(
310 M, NewTy, GV.isConstant(), GV.getLinkage(), LegalizedInitializer,
311 /*Name=*/"", &GV, GV.getThreadLocalMode(), GV.getAddressSpace(),
312 GV.isExternallyInitialized());
313 NewGV->copyAttributesFrom(&GV);
314 NewGV->copyMetadata(&GV, 0);
315 NewGV->setComdat(GV.getComdat());
316 NewGV->setAlignment(GV.getAlign());
317 GlobalMap[&GV] = NewGV;
318 OldGlobals.push_back(&GV);
319 Modified = true;
320 }
321
322 // Second pass: replace uses, transfer names, and erase old globals.
323 for (GlobalVariable *GV : OldGlobals) {
324 GlobalVariable *NewGV = GlobalMap[GV];
325 GV->replaceAllUsesWith(ConstantExpr::getBitCast(NewGV, GV->getType()));
326 NewGV->takeName(GV);
327 GV->eraseFromParent();
328 }
329
330 for (Function &F : M)
331 for (Instruction &I : instructions(F))
332 visit(I);
333
334 for (Instruction *I : ToErase)
335 I->eraseFromParent();
336
337 return Modified;
338}
339
340} // namespace
341
344 SPIRVLegalizeZeroSizeArraysImpl Impl(TM);
345 if (Impl.runOnModule(M))
347 return PreservedAnalyses::all();
348}
349
350char SPIRVLegalizeZeroSizeArraysLegacy::ID = 0;
351
352INITIALIZE_PASS(SPIRVLegalizeZeroSizeArraysLegacy,
353 "spirv-legalize-zero-size-arrays",
354 "Legalize SPIR-V zero-size arrays", false, false)
355
358 return new SPIRVLegalizeZeroSizeArraysLegacy(TM);
359}
aarch64 promote const
Expand Atomic instructions
This file defines the DenseMap class.
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
void visit(MachineFunction &MF, MachineBasicBlock &Start, std::function< void(MachineBasicBlock *)> op)
This file defines the SmallVector class.
an instruction to allocate memory on the stack
Align getAlign() const
Return the alignment of the memory that is being allocated by the instruction.
Type * getAllocatedType() const
Return the type that is being allocated by the instruction.
void setAlignment(Align Align)
ConstantArray - Constant Array Declarations.
Definition Constants.h:438
static LLVM_ABI Constant * get(ArrayType *T, ArrayRef< Constant * > V)
A constant value that is initialized with an expression using other constant values.
Definition Constants.h:1130
static LLVM_ABI Constant * getBitCast(Constant *C, Type *Ty, bool OnlyIfReduced=false)
static LLVM_ABI Constant * get(StructType *T, ArrayRef< Constant * > V)
This is an important base class in LLVM.
Definition Constant.h:43
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
This instruction extracts a struct member or array element value from an aggregate value.
LLVM_ABI void copyMetadata(const GlobalObject *Src, unsigned Offset)
Copy metadata from Src, adjusting offsets by Offset.
LLVM_ABI void setComdat(Comdat *C)
Definition Globals.cpp:214
PointerType * getType() const
Global values are always pointers.
LLVM_ABI void copyAttributesFrom(const GlobalVariable *Src)
copyAttributesFrom - copy all additional attributes (those not needed to create a GlobalVariable) fro...
Definition Globals.cpp:553
void setAlignment(Align Align)
Sets the alignment attribute of the GlobalVariable.
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2794
This instruction inserts a struct field of array element value into an aggregate value.
Value * getInsertedValueOperand()
Base class for instruction visitors.
Definition InstVisitor.h:78
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
void setDebugLoc(DebugLoc Loc)
Set the debug location information for this instruction.
An instruction for reading from memory.
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
static LLVM_ABI PointerType * get(Type *ElementType, unsigned AddressSpace)
This constructs a pointer to an object of the specified type in a numbered address space.
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM)
const SPIRVSubtarget * getSubtargetImpl() const
This class represents the LLVM 'select' instruction.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
Class to represent struct types.
static LLVM_ABI StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition Type.cpp:413
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:619
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:45
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:256
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:322
LLVM_ABI void takeName(Value *V)
Transfer the name from V to this value.
Definition Value.cpp:403
Changed
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
This is an optimization pass for GlobalISel generic memory operations.
Definition Types.h:26
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
auto dyn_cast_if_present(const Y &Val)
dyn_cast_if_present<X> - Functionally identical to dyn_cast, except that a null (or none in the case ...
Definition Casting.h:732
constexpr unsigned storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC)
Definition SPIRVUtils.h:244
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
ModulePass * createSPIRVLegalizeZeroSizeArraysPass(const SPIRVTargetMachine &TM)
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39