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
24#define DEBUG_TYPE "spirv-legalize-zero-size-arrays"
25
26using namespace llvm;
27
28namespace {
29
30bool hasZeroSizeArray(const Type *Ty) {
31 if (const ArrayType *ArrTy = dyn_cast<ArrayType>(Ty)) {
32 if (ArrTy->getNumElements() == 0)
33 return true;
34 return hasZeroSizeArray(ArrTy->getElementType());
35 }
36
37 if (const StructType *StructTy = dyn_cast<StructType>(Ty)) {
38 for (Type *ElemTy : StructTy->elements()) {
39 if (hasZeroSizeArray(ElemTy))
40 return true;
41 }
42 }
43
44 return false;
45}
46
47bool shouldLegalizeInstType(const Type *Ty) {
48 // This recursive function will always terminate because we only look inside
49 // array types, and those can't be recursive.
50 if (const ArrayType *ArrTy = dyn_cast_if_present<ArrayType>(Ty)) {
51 return ArrTy->getNumElements() == 0 ||
52 shouldLegalizeInstType(ArrTy->getElementType());
53 }
54 return false;
55}
56
57class SPIRVLegalizeZeroSizeArraysImpl
58 : public InstVisitor<SPIRVLegalizeZeroSizeArraysImpl> {
59 friend class InstVisitor<SPIRVLegalizeZeroSizeArraysImpl>;
60
61public:
62 SPIRVLegalizeZeroSizeArraysImpl(const SPIRVTargetMachine &TM)
63 : InstVisitor(), TM(TM) {}
64 bool runOnModule(Module &M);
65
66 // TODO: Handle GEP, PHI.
67 void visitAllocaInst(AllocaInst &AI);
68 void visitLoadInst(LoadInst &LI);
69 void visitStoreInst(StoreInst &SI);
70 void visitSelectInst(SelectInst &Sel);
71 void visitExtractValueInst(ExtractValueInst &EVI);
72 void visitInsertValueInst(InsertValueInst &IVI);
73
74private:
75 Type *legalizeType(Type *Ty);
76 Constant *legalizeConstant(Constant *C);
77
78 const SPIRVTargetMachine &TM;
82 bool Modified = false;
83};
84
85class SPIRVLegalizeZeroSizeArraysLegacy : public ModulePass {
86public:
87 static char ID;
88 SPIRVLegalizeZeroSizeArraysLegacy(const SPIRVTargetMachine &TM)
89 : ModulePass(ID), TM(TM) {}
90 StringRef getPassName() const override {
91 return "SPIRV Legalize Zero-Size Arrays";
92 }
93 bool runOnModule(Module &M) override {
94 SPIRVLegalizeZeroSizeArraysImpl Impl(TM);
95 return Impl.runOnModule(M);
96 }
97
98private:
99 const SPIRVTargetMachine &TM;
100};
101
102// Legalize a type. There are only two cases we need to care about:
103// arrays and structs.
104//
105// For arrays, we just replace the entire array type with a ptr.
106//
107// For structs, we create a new type with any members containing
108// nested arrays legalized.
109
110Type *SPIRVLegalizeZeroSizeArraysImpl::legalizeType(Type *Ty) {
111 auto It = TypeMap.find(Ty);
112 if (It != TypeMap.end())
113 return It->second;
114
115 Type *LegalizedTy = Ty;
116
117 if (isa<ArrayType>(Ty)) {
118 LegalizedTy = PointerType::get(
119 Ty->getContext(),
120 storageClassToAddressSpace(SPIRV::StorageClass::Generic));
121
122 } else if (StructType *StructTy = dyn_cast<StructType>(Ty)) {
123 SmallVector<Type *, 8> ElemTypes;
124 bool Changed = false;
125 for (Type *ElemTy : StructTy->elements()) {
126 Type *LegalizedElemTy = legalizeType(ElemTy);
127 ElemTypes.push_back(LegalizedElemTy);
128 Changed |= LegalizedElemTy != ElemTy;
129 }
130 if (Changed) {
131 LegalizedTy =
132 StructTy->hasName()
133 ? StructType::create(StructTy->getContext(), ElemTypes,
134 (StructTy->getName() + ".legalized").str(),
135 StructTy->isPacked())
136 : StructType::get(StructTy->getContext(), ElemTypes,
137 StructTy->isPacked());
138 }
139 }
140
141 TypeMap[Ty] = LegalizedTy;
142 return LegalizedTy;
143}
144
145Constant *SPIRVLegalizeZeroSizeArraysImpl::legalizeConstant(Constant *C) {
146 if (!C || !hasZeroSizeArray(C->getType()))
147 return C;
148
150 if (GlobalVariable *NewGV = GlobalMap.lookup(GV))
151 return NewGV;
152 return C;
153 }
154
155 Type *NewTy = legalizeType(C->getType());
156 if (isa<UndefValue>(C))
157 return PoisonValue::get(NewTy);
159 return Constant::getNullValue(NewTy);
162 for (Use &U : CA->operands())
163 Elems.push_back(legalizeConstant(cast<Constant>(U)));
164 return ConstantArray::get(cast<ArrayType>(NewTy), Elems);
165 }
166
169 for (Use &U : CS->operands())
170 Fields.push_back(legalizeConstant(cast<Constant>(U)));
171 return ConstantStruct::get(cast<StructType>(NewTy), Fields);
172 }
173
175 // Don't legalize GEP constant expressions, the backend deals with them
176 // fine.
177 if (CE->getOpcode() == Instruction::GetElementPtr)
178 return CE;
180 bool Changed = false;
181 for (Use &U : CE->operands()) {
182 Constant *LegalizedOp = legalizeConstant(cast<Constant>(U));
183 Ops.push_back(LegalizedOp);
184 Changed |= LegalizedOp != cast<Constant>(U.get());
185 }
186 if (Changed)
187 return CE->getWithOperands(Ops);
188 }
189
190 return C;
191}
192
193void SPIRVLegalizeZeroSizeArraysImpl::visitAllocaInst(AllocaInst &AI) {
194 // Check if allocation size is known-zero
195 const DataLayout &DL = AI.getModule()->getDataLayout();
196 std::optional<TypeSize> Size = AI.getAllocationSize(DL);
197 if (!Size || !Size->isZero())
198 return;
199
200 // Allocate a byte instead of an empty alloca.
201 IRBuilder<> Builder(&AI);
202 AllocaInst *NewAI = Builder.CreateAlloca(Builder.getInt8Ty());
203 NewAI->takeName(&AI);
204 NewAI->setAlignment(AI.getAlign());
205 NewAI->setDebugLoc(AI.getDebugLoc());
206 AI.replaceAllUsesWith(NewAI);
207 ToErase.push_back(&AI);
208 Modified = true;
209}
210
211void SPIRVLegalizeZeroSizeArraysImpl::visitLoadInst(LoadInst &LI) {
212 if (!hasZeroSizeArray(LI.getType()))
213 return;
214
215 // TODO: Handle structs containing zero-size arrays.
217 if (shouldLegalizeInstType(ArrTy)) {
219 ToErase.push_back(&LI);
220 Modified = true;
221 }
222}
223
224void SPIRVLegalizeZeroSizeArraysImpl::visitStoreInst(StoreInst &SI) {
225 Type *StoreTy = SI.getValueOperand()->getType();
226
227 // TODO: Handle structs containing zero-size arrays.
228 ArrayType *ArrTy = dyn_cast<ArrayType>(StoreTy);
229 if (shouldLegalizeInstType(ArrTy)) {
230 ToErase.push_back(&SI);
231 Modified = true;
232 }
233}
234
235void SPIRVLegalizeZeroSizeArraysImpl::visitSelectInst(SelectInst &Sel) {
236 if (!hasZeroSizeArray(Sel.getType()))
237 return;
238
239 // TODO: Handle structs containing zero-size arrays.
240 ArrayType *ArrTy = dyn_cast<ArrayType>(Sel.getType());
241 if (shouldLegalizeInstType(ArrTy)) {
243 ToErase.push_back(&Sel);
244 Modified = true;
245 }
246}
247
248void SPIRVLegalizeZeroSizeArraysImpl::visitExtractValueInst(
249 ExtractValueInst &EVI) {
250 if (!hasZeroSizeArray(EVI.getAggregateOperand()->getType()))
251 return;
252
253 // TODO: Handle structs containing zero-size arrays.
254 ArrayType *ArrTy = dyn_cast<ArrayType>(EVI.getType());
255 if (shouldLegalizeInstType(ArrTy)) {
257 ToErase.push_back(&EVI);
258 Modified = true;
259 }
260}
261
262void SPIRVLegalizeZeroSizeArraysImpl::visitInsertValueInst(
263 InsertValueInst &IVI) {
264 if (!hasZeroSizeArray(IVI.getAggregateOperand()->getType()))
265 return;
266
267 // TODO: Handle structs containing zero-size arrays.
268 ArrayType *ArrTy =
270 if (shouldLegalizeInstType(ArrTy)) {
272 ToErase.push_back(&IVI);
273 Modified = true;
274 }
275}
276
277bool SPIRVLegalizeZeroSizeArraysImpl::runOnModule(Module &M) {
278 TypeMap.clear();
279 GlobalMap.clear();
280 ToErase.clear();
281 Modified = false;
282
283 // Runtime arrays are allowed for shaders, so we don't need to do anything.
284 if (TM.getSubtargetImpl()->isShader())
285 return false;
286 // 0-sized arrays are handled differently for AMDGCN flavoured SPIRV.
287 if (M.getTargetTriple().getVendor() == Triple::VendorType::AMD)
288 return false;
289
290 // First pass: create new globals (legalizing the initializer as needed) and
291 // track mapping (don't erase old ones yet).
293 for (GlobalVariable &GV : M.globals()) {
294 if (!hasZeroSizeArray(GV.getValueType()))
295 continue;
296
297 Type *NewTy = legalizeType(GV.getValueType());
298 Constant *LegalizedInitializer =
299 GV.hasInitializer() && !GV.hasAppendingLinkage()
300 ? legalizeConstant(GV.getInitializer())
301 : nullptr;
302
303 // The new global will have the same linkage type as the original,
304 // except in the case that it is an llvm intrinsic global such as
305 // llvm.global_ctors with appending linkage, in which case we need to change
306 // the linkage as appending linkage is only allowed for arrays.
308 GV.hasAppendingLinkage()
310 : GV.getLinkage();
311
312 // Use an empty name for now, we will update it in the
313 // following step.
314 GlobalVariable *NewGV = new GlobalVariable(
315 M, NewTy, GV.isConstant(), NewLT, LegalizedInitializer,
316 /*Name=*/"", &GV, GV.getThreadLocalMode(), GV.getAddressSpace(),
317 GV.isExternallyInitialized());
318 NewGV->copyAttributesFrom(&GV);
319 NewGV->copyMetadata(&GV, 0);
320 NewGV->setComdat(GV.getComdat());
321 NewGV->setAlignment(GV.getAlign());
322 GlobalMap[&GV] = NewGV;
323 OldGlobals.push_back(&GV);
324 Modified = true;
325 }
326
327 // Second pass: replace uses, transfer names, and erase old globals.
328 for (GlobalVariable *GV : OldGlobals) {
329 GlobalVariable *NewGV = GlobalMap[GV];
330 GV->replaceAllUsesWith(ConstantExpr::getBitCast(NewGV, GV->getType()));
331 NewGV->takeName(GV);
332 GV->eraseFromParent();
333 }
334
335 for (Function &F : M)
336 for (Instruction &I : instructions(F))
337 visit(I);
338
339 for (Instruction *I : ToErase)
340 I->eraseFromParent();
341
342 return Modified;
343}
344
345} // namespace
346
347PreservedAnalyses
349 SPIRVLegalizeZeroSizeArraysImpl Impl(TM);
350 if (Impl.runOnModule(M))
352 return PreservedAnalyses::all();
353}
354
355char SPIRVLegalizeZeroSizeArraysLegacy::ID = 0;
356
357INITIALIZE_PASS(SPIRVLegalizeZeroSizeArraysLegacy,
358 "spirv-legalize-zero-size-arrays",
359 "Legalize SPIR-V zero-size arrays", false, false)
360
363 return new SPIRVLegalizeZeroSizeArraysLegacy(TM);
364}
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:2910
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:68
const DataLayout & getDataLayout() const
Get the data layout for the module's target platform.
Definition Module.h:325
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