LLVM 23.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 // Check if allocation size is known-zero
197 const DataLayout &DL = AI.getModule()->getDataLayout();
198 std::optional<TypeSize> Size = AI.getAllocationSize(DL);
199 if (!Size || !Size->isZero())
200 return;
201
202 // Allocate a byte instead of an empty alloca.
203 IRBuilder<> Builder(&AI);
204 AllocaInst *NewAI = Builder.CreateAlloca(Builder.getInt8Ty());
205 NewAI->takeName(&AI);
206 NewAI->setAlignment(AI.getAlign());
207 NewAI->setDebugLoc(AI.getDebugLoc());
208 AI.replaceAllUsesWith(NewAI);
209 ToErase.push_back(&AI);
210 Modified = true;
211}
212
213void SPIRVLegalizeZeroSizeArraysImpl::visitLoadInst(LoadInst &LI) {
214 if (!hasZeroSizeArray(LI.getType()))
215 return;
216
217 // TODO: Handle structs containing zero-size arrays.
219 if (shouldLegalizeInstType(ArrTy)) {
221 ToErase.push_back(&LI);
222 Modified = true;
223 }
224}
225
226void SPIRVLegalizeZeroSizeArraysImpl::visitStoreInst(StoreInst &SI) {
227 Type *StoreTy = SI.getValueOperand()->getType();
228
229 // TODO: Handle structs containing zero-size arrays.
230 ArrayType *ArrTy = dyn_cast<ArrayType>(StoreTy);
231 if (shouldLegalizeInstType(ArrTy)) {
232 ToErase.push_back(&SI);
233 Modified = true;
234 }
235}
236
237void SPIRVLegalizeZeroSizeArraysImpl::visitSelectInst(SelectInst &Sel) {
238 if (!hasZeroSizeArray(Sel.getType()))
239 return;
240
241 // TODO: Handle structs containing zero-size arrays.
242 ArrayType *ArrTy = dyn_cast<ArrayType>(Sel.getType());
243 if (shouldLegalizeInstType(ArrTy)) {
245 ToErase.push_back(&Sel);
246 Modified = true;
247 }
248}
249
250void SPIRVLegalizeZeroSizeArraysImpl::visitExtractValueInst(
251 ExtractValueInst &EVI) {
252 if (!hasZeroSizeArray(EVI.getAggregateOperand()->getType()))
253 return;
254
255 // TODO: Handle structs containing zero-size arrays.
256 ArrayType *ArrTy = dyn_cast<ArrayType>(EVI.getType());
257 if (shouldLegalizeInstType(ArrTy)) {
259 ToErase.push_back(&EVI);
260 Modified = true;
261 }
262}
263
264void SPIRVLegalizeZeroSizeArraysImpl::visitInsertValueInst(
265 InsertValueInst &IVI) {
266 if (!hasZeroSizeArray(IVI.getAggregateOperand()->getType()))
267 return;
268
269 // TODO: Handle structs containing zero-size arrays.
270 ArrayType *ArrTy =
272 if (shouldLegalizeInstType(ArrTy)) {
274 ToErase.push_back(&IVI);
275 Modified = true;
276 }
277}
278
279bool SPIRVLegalizeZeroSizeArraysImpl::runOnModule(Module &M) {
280 TypeMap.clear();
281 GlobalMap.clear();
282 ToErase.clear();
283 Modified = false;
284
285 // Runtime arrays are allowed for shaders, so we don't need to do anything.
286 if (TM.getSubtargetImpl()->isShader())
287 return false;
288 // 0-sized arrays are handled differently for AMDGCN flavoured SPIRV.
289 if (M.getTargetTriple().getVendor() == Triple::VendorType::AMD)
290 return false;
291
292 // First pass: create new globals (legalizing the initializer as needed) and
293 // track mapping (don't erase old ones yet).
295 for (GlobalVariable &GV : M.globals()) {
296 if (!hasZeroSizeArray(GV.getValueType()))
297 continue;
298
299 Type *NewTy = legalizeType(GV.getValueType());
300 Constant *LegalizedInitializer = legalizeConstant(GV.getInitializer());
301
302 // Use an empty name for now, we will update it in the
303 // following step.
304 GlobalVariable *NewGV = new GlobalVariable(
305 M, NewTy, GV.isConstant(), GV.getLinkage(), LegalizedInitializer,
306 /*Name=*/"", &GV, GV.getThreadLocalMode(), GV.getAddressSpace(),
307 GV.isExternallyInitialized());
308 NewGV->copyAttributesFrom(&GV);
309 NewGV->copyMetadata(&GV, 0);
310 NewGV->setComdat(GV.getComdat());
311 NewGV->setAlignment(GV.getAlign());
312 GlobalMap[&GV] = NewGV;
313 OldGlobals.push_back(&GV);
314 Modified = true;
315 }
316
317 // Second pass: replace uses, transfer names, and erase old globals.
318 for (GlobalVariable *GV : OldGlobals) {
319 GlobalVariable *NewGV = GlobalMap[GV];
320 GV->replaceAllUsesWith(ConstantExpr::getBitCast(NewGV, GV->getType()));
321 NewGV->takeName(GV);
322 GV->eraseFromParent();
323 }
324
325 for (Function &F : M)
326 for (Instruction &I : instructions(F))
327 visit(I);
328
329 for (Instruction *I : ToErase)
330 I->eraseFromParent();
331
332 return Modified;
333}
334
335} // namespace
336
339 SPIRVLegalizeZeroSizeArraysImpl Impl(TM);
340 if (Impl.runOnModule(M))
342 return PreservedAnalyses::all();
343}
344
345char SPIRVLegalizeZeroSizeArraysLegacy::ID = 0;
346
347INITIALIZE_PASS(SPIRVLegalizeZeroSizeArraysLegacy,
348 "spirv-legalize-zero-size-arrays",
349 "Legalize SPIR-V zero-size arrays", false, false)
350
353 return new SPIRVLegalizeZeroSizeArraysLegacy(TM);
354}
aarch64 promote const
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
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.
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: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.
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:215
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:568
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:2787
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:278
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 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:247
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