LLVM 24.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
14#include "SPIRV.h"
15#include "SPIRVTargetMachine.h"
16#include "SPIRVUtils.h"
17#include "llvm/ADT/DenseMap.h"
19#include "llvm/IR/IRBuilder.h"
21#include "llvm/IR/InstVisitor.h"
22#include "llvm/Pass.h"
23#include "llvm/Support/Debug.h"
24
25#define DEBUG_TYPE "spirv-legalize-zero-size-arrays"
26
27using namespace llvm;
28
29namespace {
30
31bool hasZeroSizeArray(const Type *Ty) {
32 if (const ArrayType *ArrTy = dyn_cast<ArrayType>(Ty)) {
33 if (ArrTy->getNumElements() == 0)
34 return true;
35 return hasZeroSizeArray(ArrTy->getElementType());
36 }
37
38 if (const StructType *StructTy = dyn_cast<StructType>(Ty)) {
39 for (Type *ElemTy : StructTy->elements()) {
40 if (hasZeroSizeArray(ElemTy))
41 return true;
42 }
43 }
44
45 return false;
46}
47
48bool shouldLegalizeInstType(const Type *Ty) {
49 // This recursive function will always terminate because we only look inside
50 // array types, and those can't be recursive.
51 if (const ArrayType *ArrTy = dyn_cast_if_present<ArrayType>(Ty)) {
52 return ArrTy->getNumElements() == 0 ||
53 shouldLegalizeInstType(ArrTy->getElementType());
54 }
55 return false;
56}
57
58class SPIRVLegalizeZeroSizeArraysImpl
59 : public InstVisitor<SPIRVLegalizeZeroSizeArraysImpl> {
60 friend class InstVisitor<SPIRVLegalizeZeroSizeArraysImpl>;
61
62public:
63 SPIRVLegalizeZeroSizeArraysImpl(const SPIRVTargetMachine &TM)
64 : InstVisitor(), TM(TM) {}
65 bool runOnModule(Module &M);
66
67 // TODO: Handle GEP, PHI.
68 void visitAllocaInst(AllocaInst &AI);
69 void visitLoadInst(LoadInst &LI);
70 void visitStoreInst(StoreInst &SI);
71 void visitSelectInst(SelectInst &Sel);
72 void visitExtractValueInst(ExtractValueInst &EVI);
73 void visitInsertValueInst(InsertValueInst &IVI);
74
75private:
76 Type *legalizeType(Type *Ty);
77 Constant *legalizeConstant(Constant *C);
78
79 const SPIRVTargetMachine &TM;
83 bool Modified = false;
84};
85
86class SPIRVLegalizeZeroSizeArraysLegacy : public ModulePass {
87public:
88 static char ID;
89 SPIRVLegalizeZeroSizeArraysLegacy(const SPIRVTargetMachine &TM)
90 : ModulePass(ID), TM(TM) {}
91 StringRef getPassName() const override {
92 return "SPIRV Legalize Zero-Size Arrays";
93 }
94 bool runOnModule(Module &M) override {
95 SPIRVLegalizeZeroSizeArraysImpl Impl(TM);
96 return Impl.runOnModule(M);
97 }
98
99private:
100 const SPIRVTargetMachine &TM;
101};
102
103// Legalize a type. There are only two cases we need to care about:
104// arrays and structs.
105//
106// For arrays, we just replace the entire array type with a ptr.
107//
108// For structs, we create a new type with any members containing
109// nested arrays legalized.
110
111Type *SPIRVLegalizeZeroSizeArraysImpl::legalizeType(Type *Ty) {
112 auto It = TypeMap.find(Ty);
113 if (It != TypeMap.end())
114 return It->second;
115
116 Type *LegalizedTy = Ty;
117
118 if (isa<ArrayType>(Ty)) {
119 LegalizedTy = PointerType::get(
120 Ty->getContext(),
121 storageClassToAddressSpace(SPIRV::StorageClass::Generic));
122
123 } else if (StructType *StructTy = dyn_cast<StructType>(Ty)) {
124 SmallVector<Type *, 8> ElemTypes;
125 bool Changed = false;
126 for (Type *ElemTy : StructTy->elements()) {
127 Type *LegalizedElemTy = legalizeType(ElemTy);
128 ElemTypes.push_back(LegalizedElemTy);
129 Changed |= LegalizedElemTy != ElemTy;
130 }
131 if (Changed) {
132 LegalizedTy =
133 StructTy->hasName()
134 ? StructType::create(StructTy->getContext(), ElemTypes,
135 (StructTy->getName() + ".legalized").str(),
136 StructTy->isPacked())
137 : StructType::get(StructTy->getContext(), ElemTypes,
138 StructTy->isPacked());
139 }
140 }
141
142 TypeMap[Ty] = LegalizedTy;
143 return LegalizedTy;
144}
145
146Constant *SPIRVLegalizeZeroSizeArraysImpl::legalizeConstant(Constant *C) {
147 if (!C || !hasZeroSizeArray(C->getType()))
148 return C;
149
151 if (GlobalVariable *NewGV = GlobalMap.lookup(GV))
152 return NewGV;
153 return C;
154 }
155
156 Type *NewTy = legalizeType(C->getType());
157 if (isa<UndefValue>(C))
158 return PoisonValue::get(NewTy);
160 return Constant::getNullValue(NewTy);
163 for (Use &U : CA->operands())
164 Elems.push_back(legalizeConstant(cast<Constant>(U)));
165 return ConstantArray::get(cast<ArrayType>(NewTy), Elems);
166 }
167
170 for (Use &U : CS->operands())
171 Fields.push_back(legalizeConstant(cast<Constant>(U)));
172 return ConstantStruct::get(cast<StructType>(NewTy), Fields);
173 }
174
176 // Don't legalize GEP constant expressions, the backend deals with them
177 // fine.
178 if (CE->getOpcode() == Instruction::GetElementPtr)
179 return CE;
181 bool Changed = false;
182 for (Use &U : CE->operands()) {
183 Constant *LegalizedOp = legalizeConstant(cast<Constant>(U));
184 Ops.push_back(LegalizedOp);
185 Changed |= LegalizedOp != cast<Constant>(U.get());
186 }
187 if (Changed)
188 return CE->getWithOperands(Ops);
189 }
190
191 return C;
192}
193
194void SPIRVLegalizeZeroSizeArraysImpl::visitAllocaInst(AllocaInst &AI) {
195 // Check if allocation size is known-zero
196 const DataLayout &DL = AI.getModule()->getDataLayout();
197 std::optional<TypeSize> Size = AI.getAllocationSize(DL);
198 if (!Size || !Size->isZero())
199 return;
200
201 // Allocate a byte instead of an empty alloca.
202 IRBuilder<> Builder(&AI);
203 AllocaInst *NewAI = Builder.CreateAlloca(Builder.getInt8Ty());
204 NewAI->takeName(&AI);
205 NewAI->setAlignment(AI.getAlign());
206 NewAI->setDebugLoc(AI.getDebugLoc());
207 AI.replaceAllUsesWith(NewAI);
208 ToErase.push_back(&AI);
209 Modified = true;
210}
211
212void SPIRVLegalizeZeroSizeArraysImpl::visitLoadInst(LoadInst &LI) {
213 if (!hasZeroSizeArray(LI.getType()))
214 return;
215
216 // TODO: Handle structs containing zero-size arrays.
218 if (shouldLegalizeInstType(ArrTy)) {
220 ToErase.push_back(&LI);
221 Modified = true;
222 }
223}
224
225void SPIRVLegalizeZeroSizeArraysImpl::visitStoreInst(StoreInst &SI) {
226 Type *StoreTy = SI.getValueOperand()->getType();
227
228 // TODO: Handle structs containing zero-size arrays.
229 ArrayType *ArrTy = dyn_cast<ArrayType>(StoreTy);
230 if (shouldLegalizeInstType(ArrTy)) {
231 ToErase.push_back(&SI);
232 Modified = true;
233 }
234}
235
236void SPIRVLegalizeZeroSizeArraysImpl::visitSelectInst(SelectInst &Sel) {
237 if (!hasZeroSizeArray(Sel.getType()))
238 return;
239
240 // TODO: Handle structs containing zero-size arrays.
241 ArrayType *ArrTy = dyn_cast<ArrayType>(Sel.getType());
242 if (shouldLegalizeInstType(ArrTy)) {
244 ToErase.push_back(&Sel);
245 Modified = true;
246 }
247}
248
249void SPIRVLegalizeZeroSizeArraysImpl::visitExtractValueInst(
250 ExtractValueInst &EVI) {
251 if (!hasZeroSizeArray(EVI.getAggregateOperand()->getType()))
252 return;
253
254 // TODO: Handle structs containing zero-size arrays.
255 ArrayType *ArrTy = dyn_cast<ArrayType>(EVI.getType());
256 if (shouldLegalizeInstType(ArrTy)) {
258 ToErase.push_back(&EVI);
259 Modified = true;
260 }
261}
262
263void SPIRVLegalizeZeroSizeArraysImpl::visitInsertValueInst(
264 InsertValueInst &IVI) {
265 if (!hasZeroSizeArray(IVI.getAggregateOperand()->getType()))
266 return;
267
268 // TODO: Handle structs containing zero-size arrays.
269 ArrayType *ArrTy =
271 if (shouldLegalizeInstType(ArrTy)) {
273 ToErase.push_back(&IVI);
274 Modified = true;
275 }
276}
277
278bool SPIRVLegalizeZeroSizeArraysImpl::runOnModule(Module &M) {
279 TypeMap.clear();
280 GlobalMap.clear();
281 ToErase.clear();
282 Modified = false;
283
284 // Runtime arrays are allowed for shaders, so we don't need to do anything.
285 if (TM.getSubtargetImpl()->isShader())
286 return false;
287 // 0-sized arrays are handled differently for AMDGCN flavoured SPIRV.
288 if (M.getTargetTriple().getVendor() == Triple::VendorType::AMD)
289 return false;
290
291 // First pass: create new globals (legalizing the initializer as needed) and
292 // track mapping (don't erase old ones yet).
294 for (GlobalVariable &GV : M.globals()) {
295 if (!hasZeroSizeArray(GV.getValueType()))
296 continue;
297
298 Type *NewTy = legalizeType(GV.getValueType());
299 Constant *LegalizedInitializer =
300 GV.hasInitializer() && !GV.hasAppendingLinkage()
301 ? legalizeConstant(GV.getInitializer())
302 : nullptr;
303
304 // The new global will have the same linkage type as the original,
305 // except in the case that it is an llvm intrinsic global such as
306 // llvm.global_ctors with appending linkage, in which case we need to change
307 // the linkage as appending linkage is only allowed for arrays.
309 GV.hasAppendingLinkage()
311 : GV.getLinkage();
312
313 // Use an empty name for now, we will update it in the
314 // following step.
315 GlobalVariable *NewGV = new GlobalVariable(
316 M, NewTy, GV.isConstant(), NewLT, LegalizedInitializer,
317 /*Name=*/"", &GV, GV.getThreadLocalMode(), GV.getAddressSpace(),
318 GV.isExternallyInitialized());
319 NewGV->copyAttributesFrom(&GV);
320 NewGV->copyMetadata(&GV, 0);
321 NewGV->setComdat(GV.getComdat());
322 NewGV->setAlignment(GV.getAlign());
323 GlobalMap[&GV] = NewGV;
324 OldGlobals.push_back(&GV);
325 Modified = true;
326 }
327
328 // Second pass: replace uses, transfer names, and erase old globals.
329 for (GlobalVariable *GV : OldGlobals) {
330 GlobalVariable *NewGV = GlobalMap[GV];
331 GV->replaceAllUsesWith(ConstantExpr::getBitCast(NewGV, GV->getType()));
332 NewGV->takeName(GV);
333 GV->eraseFromParent();
334 }
335
336 for (Function &F : M)
337 for (Instruction &I : instructions(F))
338 visit(I);
339
340 for (Instruction *I : ToErase)
341 I->eraseFromParent();
342
343 return Modified;
344}
345
346} // namespace
347
348PreservedAnalyses
350 SPIRVLegalizeZeroSizeArraysImpl Impl(TM);
351 if (Impl.runOnModule(M))
353 return PreservedAnalyses::all();
354}
355
356char SPIRVLegalizeZeroSizeArraysLegacy::ID = 0;
357
358INITIALIZE_PASS(SPIRVLegalizeZeroSizeArraysLegacy,
359 "spirv-legalize-zero-size-arrays",
360 "Legalize SPIR-V zero-size arrays", false, false)
361
364 return new SPIRVLegalizeZeroSizeArraysLegacy(TM);
365}
aarch64 promote const
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
Expand Atomic instructions
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
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
static void visit(BasicBlock &Start, std::function< bool(BasicBlock *)> 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.
LLVM_ABI std::optional< TypeSize > getAllocationSize(const DataLayout &DL) const
Get allocation size in bytes.
void setAlignment(Align Align)
ConstantArray - Constant Array Declarations.
Definition Constants.h:590
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:1316
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.
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
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:287
PointerType * getType() const
Global values are always pointers.
LinkageTypes
An enumeration for the kinds of linkage for global values.
Definition GlobalValue.h:52
@ ExternalWeakLinkage
ExternalWeak linkage description.
Definition GlobalValue.h:62
LLVM_ABI void copyAttributesFrom(const GlobalVariable *Src)
copyAttributesFrom - copy all additional attributes (those not needed to create a GlobalVariable) fro...
Definition Globals.cpp:647
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:2903
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.
LLVM_ABI const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
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
const DataLayout & getDataLayout() const
Get the data layout for the module's target platform.
Definition Module.h:320
static LLVM_ABI PointerType * get(LLVMContext &C, unsigned AddressSpace)
This constructs an opaque pointer to an object in a numbered address space.
Definition Type.cpp:911
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
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.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
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:477
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:683
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
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:255
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
LLVM_ABI void takeName(Value *V)
Transfer the name from V to this value.
Definition Value.cpp:400
Changed
This is an optimization pass for GlobalISel generic memory operations.
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:245
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