2 // Copyright (C) 2014-2015 LunarG, Inc.
3 // Copyright (C) 2015-2020 Google, Inc.
4 // Copyright (C) 2017 ARM Limited.
5 // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
7 // All rights reserved.
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions
13 // Redistributions of source code must retain the above copyright
14 // notice, this list of conditions and the following disclaimer.
16 // Redistributions in binary form must reproduce the above
17 // copyright notice, this list of conditions and the following
18 // disclaimer in the documentation and/or other materials provided
19 // with the distribution.
21 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its
22 // contributors may be used to endorse or promote products derived
23 // from this software without specific prior written permission.
25 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 // POSSIBILITY OF SUCH DAMAGE.
39 // "Builder" is an interface to fully build SPIR-V IR. Allocate one of
40 // these to build (a thread safe) internal SPIR-V representation (IR),
41 // and then dump it as a binary stream according to the SPIR-V specification.
43 // A Builder has a 1:1 relationship with a SPIR-V module.
60 #include <unordered_map>
67 Spv_1_1 = (1 << 16) | (1 << 8),
68 Spv_1_2 = (1 << 16) | (2 << 8),
69 Spv_1_3 = (1 << 16) | (3 << 8),
70 Spv_1_4 = (1 << 16) | (4 << 8),
71 Spv_1_5 = (1 << 16) | (5 << 8),
76 Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger);
79 static const int maxMatrixSize = 4;
81 unsigned int getSpvVersion() const { return spvVersion; }
83 void setSource(spv::SourceLanguage lang, int version)
86 sourceVersion = version;
88 spv::Id getStringId(const std::string& str)
90 auto sItr = stringIds.find(str);
91 if (sItr != stringIds.end())
93 spv::Id strId = getUniqueId();
94 Instruction* fileString = new Instruction(strId, NoType, OpString);
95 const char* file_c_str = str.c_str();
96 fileString->addStringOperand(file_c_str);
97 strings.push_back(std::unique_ptr<Instruction>(fileString));
98 module.mapInstruction(fileString);
99 stringIds[file_c_str] = strId;
102 spv::Id getSourceFile() const
104 return sourceFileStringId;
106 void setSourceFile(const std::string& file)
108 sourceFileStringId = getStringId(file);
110 void setSourceText(const std::string& text) { sourceText = text; }
111 void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); }
112 void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); }
113 void setEmitOpLines() { emitOpLines = true; }
114 void addExtension(const char* ext) { extensions.insert(ext); }
115 void removeExtension(const char* ext)
117 extensions.erase(ext);
119 void addIncorporatedExtension(const char* ext, SpvVersion incorporatedVersion)
121 if (getSpvVersion() < static_cast<unsigned>(incorporatedVersion))
124 void promoteIncorporatedExtension(const char* baseExt, const char* promoExt, SpvVersion incorporatedVersion)
126 removeExtension(baseExt);
127 addIncorporatedExtension(promoExt, incorporatedVersion);
129 void addInclude(const std::string& name, const std::string& text)
131 spv::Id incId = getStringId(name);
132 includeFiles[incId] = &text;
134 Id import(const char*);
135 void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem)
141 void addCapability(spv::Capability cap) { capabilities.insert(cap); }
143 // To get a new <id> for anything needing a new one.
144 Id getUniqueId() { return ++uniqueId; }
146 // To get a set of new <id>s, e.g., for a set of function parameters
147 Id getUniqueIds(int numIds)
149 Id id = uniqueId + 1;
154 // Generate OpLine for non-filename-based #line directives (ie no filename
155 // seen yet): Log the current line, and if different than the last one,
156 // issue a new OpLine using the new line and current source file name.
157 void setLine(int line);
159 // If filename null, generate OpLine for non-filename-based line directives,
160 // else do filename-based: Log the current line and file, and if different
161 // than the last one, issue a new OpLine using the new line and file
163 void setLine(int line, const char* filename);
164 // Low-level OpLine. See setLine() for a layered helper.
165 void addLine(Id fileName, int line, int column);
167 // For creating new types (will return old type if the requested one was already made).
170 Id makePointer(StorageClass, Id pointee);
171 Id makeForwardPointer(StorageClass);
172 Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee);
173 Id makeIntegerType(int width, bool hasSign); // generic
174 Id makeIntType(int width) { return makeIntegerType(width, true); }
175 Id makeUintType(int width) { return makeIntegerType(width, false); }
176 Id makeFloatType(int width);
177 Id makeStructType(const std::vector<Id>& members, const char*);
178 Id makeStructResultType(Id type0, Id type1);
179 Id makeVectorType(Id component, int size);
180 Id makeMatrixType(Id component, int cols, int rows);
181 Id makeArrayType(Id element, Id sizeId, int stride); // 0 stride means no stride decoration
182 Id makeRuntimeArray(Id element);
183 Id makeFunctionType(Id returnType, const std::vector<Id>& paramTypes);
184 Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format);
185 Id makeSamplerType();
186 Id makeSampledImageType(Id imageType);
187 Id makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols);
188 Id makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands);
190 // accelerationStructureNV type
191 Id makeAccelerationStructureType();
193 Id makeRayQueryType();
195 // For querying about types.
196 Id getTypeId(Id resultId) const { return module.getTypeId(resultId); }
197 Id getDerefTypeId(Id resultId) const;
198 Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); }
199 Op getTypeClass(Id typeId) const { return getOpCode(typeId); }
200 Op getMostBasicTypeClass(Id typeId) const;
201 int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); }
202 int getNumTypeConstituents(Id typeId) const;
203 int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); }
204 Id getScalarTypeId(Id typeId) const;
205 Id getContainedTypeId(Id typeId) const;
206 Id getContainedTypeId(Id typeId, int) const;
207 StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); }
208 ImageFormat getImageTypeFormat(Id typeId) const
209 { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); }
210 Id getResultingAccessChainType() const;
212 bool isPointer(Id resultId) const { return isPointerType(getTypeId(resultId)); }
213 bool isScalar(Id resultId) const { return isScalarType(getTypeId(resultId)); }
214 bool isVector(Id resultId) const { return isVectorType(getTypeId(resultId)); }
215 bool isMatrix(Id resultId) const { return isMatrixType(getTypeId(resultId)); }
216 bool isCooperativeMatrix(Id resultId)const { return isCooperativeMatrixType(getTypeId(resultId)); }
217 bool isAggregate(Id resultId) const { return isAggregateType(getTypeId(resultId)); }
218 bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); }
220 bool isBoolType(Id typeId)
221 { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); }
222 bool isIntType(Id typeId) const
223 { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; }
224 bool isUintType(Id typeId) const
225 { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; }
226 bool isFloatType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat; }
227 bool isPointerType(Id typeId) const { return getTypeClass(typeId) == OpTypePointer; }
228 bool isScalarType(Id typeId) const
229 { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt ||
230 getTypeClass(typeId) == OpTypeBool; }
231 bool isVectorType(Id typeId) const { return getTypeClass(typeId) == OpTypeVector; }
232 bool isMatrixType(Id typeId) const { return getTypeClass(typeId) == OpTypeMatrix; }
233 bool isStructType(Id typeId) const { return getTypeClass(typeId) == OpTypeStruct; }
234 bool isArrayType(Id typeId) const { return getTypeClass(typeId) == OpTypeArray; }
236 bool isCooperativeMatrixType(Id typeId)const { return false; }
238 bool isCooperativeMatrixType(Id typeId)const { return getTypeClass(typeId) == OpTypeCooperativeMatrixNV; }
240 bool isAggregateType(Id typeId) const
241 { return isArrayType(typeId) || isStructType(typeId) || isCooperativeMatrixType(typeId); }
242 bool isImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeImage; }
243 bool isSamplerType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampler; }
244 bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; }
245 bool containsType(Id typeId, Op typeOp, unsigned int width) const;
246 bool containsPhysicalStorageBufferOrArray(Id typeId) const;
248 bool isConstantOpCode(Op opcode) const;
249 bool isSpecConstantOpCode(Op opcode) const;
250 bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); }
251 bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }
252 bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); }
253 unsigned int getConstantScalar(Id resultId) const
254 { return module.getInstruction(resultId)->getImmediateOperand(0); }
255 StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); }
257 bool isVariableOpCode(Op opcode) const { return opcode == OpVariable; }
258 bool isVariable(Id resultId) const { return isVariableOpCode(getOpCode(resultId)); }
259 bool isGlobalStorage(Id resultId) const { return getStorageClass(resultId) != StorageClassFunction; }
260 bool isGlobalVariable(Id resultId) const { return isVariable(resultId) && isGlobalStorage(resultId); }
261 // See if a resultId is valid for use as an initializer.
262 bool isValidInitializer(Id resultId) const { return isConstant(resultId) || isGlobalVariable(resultId); }
264 int getScalarTypeWidth(Id typeId) const
266 Id scalarTypeId = getScalarTypeId(typeId);
267 assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat);
268 return module.getInstruction(scalarTypeId)->getImmediateOperand(0);
271 int getTypeNumColumns(Id typeId) const
273 assert(isMatrixType(typeId));
274 return getNumTypeConstituents(typeId);
276 int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); }
277 int getTypeNumRows(Id typeId) const
279 assert(isMatrixType(typeId));
280 return getNumTypeComponents(getContainedTypeId(typeId));
282 int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); }
284 Dim getTypeDimensionality(Id typeId) const
286 assert(isImageType(typeId));
287 return (Dim)module.getInstruction(typeId)->getImmediateOperand(1);
289 Id getImageType(Id resultId) const
291 Id typeId = getTypeId(resultId);
292 assert(isImageType(typeId) || isSampledImageType(typeId));
293 return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId;
295 bool isArrayedImageType(Id typeId) const
297 assert(isImageType(typeId));
298 return module.getInstruction(typeId)->getImmediateOperand(3) != 0;
301 // For making new constants (will return old constant if the requested one was already made).
302 Id makeNullConstant(Id typeId);
303 Id makeBoolConstant(bool b, bool specConstant = false);
304 Id makeInt8Constant(int i, bool specConstant = false)
305 { return makeIntConstant(makeIntType(8), (unsigned)i, specConstant); }
306 Id makeUint8Constant(unsigned u, bool specConstant = false)
307 { return makeIntConstant(makeUintType(8), u, specConstant); }
308 Id makeInt16Constant(int i, bool specConstant = false)
309 { return makeIntConstant(makeIntType(16), (unsigned)i, specConstant); }
310 Id makeUint16Constant(unsigned u, bool specConstant = false)
311 { return makeIntConstant(makeUintType(16), u, specConstant); }
312 Id makeIntConstant(int i, bool specConstant = false)
313 { return makeIntConstant(makeIntType(32), (unsigned)i, specConstant); }
314 Id makeUintConstant(unsigned u, bool specConstant = false)
315 { return makeIntConstant(makeUintType(32), u, specConstant); }
316 Id makeInt64Constant(long long i, bool specConstant = false)
317 { return makeInt64Constant(makeIntType(64), (unsigned long long)i, specConstant); }
318 Id makeUint64Constant(unsigned long long u, bool specConstant = false)
319 { return makeInt64Constant(makeUintType(64), u, specConstant); }
320 Id makeFloatConstant(float f, bool specConstant = false);
321 Id makeDoubleConstant(double d, bool specConstant = false);
322 Id makeFloat16Constant(float f16, bool specConstant = false);
323 Id makeFpConstant(Id type, double d, bool specConstant = false);
325 // Turn the array of constants into a proper spv constant of the requested type.
326 Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false);
328 // Methods for adding information outside the CFG.
329 Instruction* addEntryPoint(ExecutionModel, Function*, const char* name);
330 void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1);
331 void addExecutionMode(Function*, ExecutionMode mode, const std::vector<unsigned>& literals);
332 void addExecutionModeId(Function*, ExecutionMode mode, const std::vector<Id>& operandIds);
333 void addName(Id, const char* name);
334 void addMemberName(Id, int member, const char* name);
335 void addDecoration(Id, Decoration, int num = -1);
336 void addDecoration(Id, Decoration, const char*);
337 void addDecoration(Id, Decoration, const std::vector<unsigned>& literals);
338 void addDecoration(Id, Decoration, const std::vector<const char*>& strings);
339 void addDecorationId(Id id, Decoration, Id idDecoration);
340 void addDecorationId(Id id, Decoration, const std::vector<Id>& operandIds);
341 void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
342 void addMemberDecoration(Id, unsigned int member, Decoration, const char*);
343 void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<unsigned>& literals);
344 void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<const char*>& strings);
346 // At the end of what block do the next create*() instructions go?
347 void setBuildPoint(Block* bp) { buildPoint = bp; }
348 Block* getBuildPoint() const { return buildPoint; }
350 // Make the entry-point function. The returned pointer is only valid
351 // for the lifetime of this builder.
352 Function* makeEntryPoint(const char*);
354 // Make a shader-style function, and create its entry block if entry is non-zero.
355 // Return the function, pass back the entry.
356 // The returned pointer is only valid for the lifetime of this builder.
357 Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name,
358 const std::vector<Id>& paramTypes, const std::vector<std::vector<Decoration>>& precisions, Block **entry = 0);
360 // Create a return. An 'implicit' return is one not appearing in the source
361 // code. In the case of an implicit return, no post-return block is inserted.
362 void makeReturn(bool implicit, Id retVal = 0);
364 // Generate all the code needed to finish up a function.
365 void leaveFunction();
367 // Create block terminator instruction for certain statements like
368 // discard, terminate-invocation, terminateRayEXT, or ignoreIntersectionEXT
369 void makeStatementTerminator(spv::Op opcode, const char *name);
371 // Create a global or function local or IO variable.
372 Id createVariable(Decoration precision, StorageClass, Id type, const char* name = nullptr,
373 Id initializer = NoResult);
375 // Create an intermediate with an undefined value.
376 Id createUndefined(Id type);
378 // Store into an Id and return the l-value
379 void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
380 spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
382 // Load from an Id and return it
383 Id createLoad(Id lValue, spv::Decoration precision,
384 spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
385 spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
387 // Create an OpAccessChain instruction
388 Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets);
390 // Create an OpArrayLength instruction
391 Id createArrayLength(Id base, unsigned int member);
393 // Create an OpCooperativeMatrixLengthNV instruction
394 Id createCooperativeMatrixLength(Id type);
396 // Create an OpCompositeExtract instruction
397 Id createCompositeExtract(Id composite, Id typeId, unsigned index);
398 Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes);
399 Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
400 Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes);
402 Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex);
403 Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex);
405 void createNoResultOp(Op);
406 void createNoResultOp(Op, Id operand);
407 void createNoResultOp(Op, const std::vector<Id>& operands);
408 void createNoResultOp(Op, const std::vector<IdImmediate>& operands);
409 void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask);
410 void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
411 Id createUnaryOp(Op, Id typeId, Id operand);
412 Id createBinOp(Op, Id typeId, Id operand1, Id operand2);
413 Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
414 Id createOp(Op, Id typeId, const std::vector<Id>& operands);
415 Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands);
416 Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&);
417 Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals);
419 // Take an rvalue (source) and a set of channels to extract from it to
420 // make a new rvalue, which is returned.
421 Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels);
423 // Take a copy of an lvalue (target) and a source of components, and set the
424 // source components into the lvalue where the 'channels' say to put them.
425 // An updated version of the target is returned.
426 // (No true lvalue or stores are used.)
427 Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels);
429 // If both the id and precision are valid, the id
430 // gets tagged with the requested precision.
431 // The passed in id is always the returned id, to simplify use patterns.
432 Id setPrecision(Id id, Decoration precision)
434 if (precision != NoPrecision && id != NoResult)
435 addDecoration(id, precision);
440 // Can smear a scalar to a vector for the following forms:
441 // - promoteScalar(scalar, vector) // smear scalar to width of vector
442 // - promoteScalar(vector, scalar) // smear scalar to width of vector
443 // - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to
444 // - promoteScalar(scalar, scalar) // do nothing
445 // Other forms are not allowed.
447 // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'.
448 // The type of the created vector is a vector of components of the same type as the scalar.
450 // Note: One of the arguments will change, with the result coming back that way rather than
451 // through the return value.
452 void promoteScalar(Decoration precision, Id& left, Id& right);
454 // Make a value by smearing the scalar to fill the type.
455 // vectorType should be the correct type for making a vector of scalarVal.
456 // (No conversions are done.)
457 Id smearScalar(Decoration precision, Id scalarVal, Id vectorType);
459 // Create a call to a built-in function.
460 Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args);
462 // List of parameters used to create a texture operation
463 struct TextureParameters {
483 // Select the correct texture operation based on all inputs, and emit the correct instruction
484 Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
485 bool noImplicit, const TextureParameters&, ImageOperandsMask);
487 // Emit the OpTextureQuery* instruction that was passed in.
488 // Figure out the right return value and type, and return it.
489 Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult);
491 Id createSamplePositionCall(Decoration precision, Id, Id);
493 Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned);
494 Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id);
496 // Reduction comparison for composites: For equal and not-equal resulting in a scalar.
497 Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */);
499 // OpCompositeConstruct
500 Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents);
502 // vector or scalar constructor
503 Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId);
505 // matrix constructor
506 Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee);
508 // Helper to use for building nested control flow with if-then-else.
511 If(Id condition, unsigned int ctrl, Builder& builder);
514 void makeBeginElse();
523 unsigned int control;
531 // Make a switch statement. A switch has 'numSegments' of pieces of code, not containing
532 // any case/default labels, all separated by one or more case/default labels. Each possible
533 // case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this
534 // number space. How to compute the value is given by 'condition', as in switch(condition).
536 // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.
538 // Use a defaultSegment < 0 if there is no default segment (to branch to post switch).
540 // Returns the right set of basic blocks to start each code segment with, so that the caller's
541 // recursion stack can hold the memory for it.
543 void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector<int>& caseValues,
544 const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB);
546 // Add a branch to the innermost switch's merge block.
547 void addSwitchBreak();
549 // Move to the next code segment, passing in the return argument in makeSwitch()
550 void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment);
552 // Finish off the innermost switch.
553 void endSwitch(std::vector<Block*>& segmentBB);
556 LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) :
557 head(head), body(body), merge(merge), continue_target(continue_target) { }
558 Block &head, &body, &merge, &continue_target;
561 LoopBlocks& operator=(const LoopBlocks&) = delete;
564 // Start a new loop and prepare the builder to generate code for it. Until
565 // closeLoop() is called for this loop, createLoopContinue() and
566 // createLoopExit() will target its corresponding blocks.
567 LoopBlocks& makeNewLoop();
569 // Create a new block in the function containing the build point. Memory is
570 // owned by the function object.
571 Block& makeNewBlock();
573 // Add a branch to the continue_target of the current (innermost) loop.
574 void createLoopContinue();
576 // Add an exit (e.g. "break") from the innermost loop that we're currently
578 void createLoopExit();
580 // Close the innermost loop that you're in
584 // Access chain design for an R-Value vs. L-Value:
586 // There is a single access chain the builder is building at
587 // any particular time. Such a chain can be used to either to a load or
588 // a store, when desired.
590 // Expressions can be r-values, l-values, or both, or only r-values:
591 // a[b.c].d = .... // l-value
592 // ... = a[b.c].d; // r-value, that also looks like an l-value
593 // ++a[b.c].d; // r-value and l-value
594 // (x + y)[2]; // r-value only, can't possibly be l-value
596 // Computing an r-value means generating code. Hence,
597 // r-values should only be computed when they are needed, not speculatively.
599 // Computing an l-value means saving away information for later use in the compiler,
600 // no code is generated until the l-value is later dereferenced. It is okay
601 // to speculatively generate an l-value, just not okay to speculatively dereference it.
603 // The base of the access chain (the left-most variable or expression
604 // from which everything is based) can be set either as an l-value
605 // or as an r-value. Most efficient would be to set an l-value if one
606 // is available. If an expression was evaluated, the resulting r-value
607 // can be set as the chain base.
609 // The users of this single access chain can save and restore if they
610 // want to nest or manage multiple chains.
614 Id base; // for l-values, pointer to the base object, for r-values, the base object
615 std::vector<Id> indexChain;
616 Id instr; // cache the instruction that generates this access chain
617 std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number
618 Id component; // a dynamic component index, can coexist with a swizzle,
619 // done after the swizzle, NoResult if not present
620 Id preSwizzleBaseType; // dereferenced type, before swizzle or component is applied;
621 // NoType unless a swizzle or component is present
622 bool isRValue; // true if 'base' is an r-value, otherwise, base is an l-value
623 unsigned int alignment; // bitwise OR of alignment values passed in. Accumulates worst alignment.
624 // Only tracks base and (optional) component selection alignment.
626 // Accumulate whether anything in the chain of structures has coherent decorations.
627 struct CoherentFlags {
628 CoherentFlags() { clear(); }
631 bool isVolatile() const { return false; }
632 CoherentFlags operator |=(const CoherentFlags &other) { return *this; }
634 bool isVolatile() const { return volatil; }
635 bool isNonUniform() const { return nonUniform; }
636 bool anyCoherent() const {
637 return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent ||
638 subgroupcoherent || shadercallcoherent;
641 unsigned coherent : 1;
642 unsigned devicecoherent : 1;
643 unsigned queuefamilycoherent : 1;
644 unsigned workgroupcoherent : 1;
645 unsigned subgroupcoherent : 1;
646 unsigned shadercallcoherent : 1;
647 unsigned nonprivate : 1;
648 unsigned volatil : 1;
649 unsigned isImage : 1;
650 unsigned nonUniform : 1;
655 queuefamilycoherent = 0;
656 workgroupcoherent = 0;
657 subgroupcoherent = 0;
658 shadercallcoherent = 0;
665 CoherentFlags operator |=(const CoherentFlags &other) {
666 coherent |= other.coherent;
667 devicecoherent |= other.devicecoherent;
668 queuefamilycoherent |= other.queuefamilycoherent;
669 workgroupcoherent |= other.workgroupcoherent;
670 subgroupcoherent |= other.subgroupcoherent;
671 shadercallcoherent |= other.shadercallcoherent;
672 nonprivate |= other.nonprivate;
673 volatil |= other.volatil;
674 isImage |= other.isImage;
675 nonUniform |= other.nonUniform;
680 CoherentFlags coherentFlags;
684 // the SPIR-V builder maintains a single active chain that
685 // the following methods operate on
688 // for external save and restore
689 AccessChain getAccessChain() { return accessChain; }
690 void setAccessChain(AccessChain newChain) { accessChain = newChain; }
693 void clearAccessChain();
695 // set new base as an l-value base
696 void setAccessChainLValue(Id lValue)
698 assert(isPointer(lValue));
699 accessChain.base = lValue;
702 // set new base value as an r-value
703 void setAccessChainRValue(Id rValue)
705 accessChain.isRValue = true;
706 accessChain.base = rValue;
709 // push offset onto the end of the chain
710 void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
712 accessChain.indexChain.push_back(offset);
713 accessChain.coherentFlags |= coherentFlags;
714 accessChain.alignment |= alignment;
717 // push new swizzle onto the end of any existing swizzle, merging into a single swizzle
718 void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,
719 AccessChain::CoherentFlags coherentFlags, unsigned int alignment);
721 // push a dynamic component selection onto the access chain, only applicable with a
722 // non-trivial swizzle or no swizzle
723 void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags,
724 unsigned int alignment)
726 if (accessChain.swizzle.size() != 1) {
727 accessChain.component = component;
728 if (accessChain.preSwizzleBaseType == NoType)
729 accessChain.preSwizzleBaseType = preSwizzleBaseType;
731 accessChain.coherentFlags |= coherentFlags;
732 accessChain.alignment |= alignment;
735 // use accessChain and swizzle to store value
736 void accessChainStore(Id rvalue, Decoration nonUniform,
737 spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
738 spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
740 // use accessChain and swizzle to load an r-value
741 Id accessChainLoad(Decoration precision, Decoration l_nonUniform, Decoration r_nonUniform, Id ResultType,
742 spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax,
743 unsigned int alignment = 0);
745 // Return whether or not the access chain can be represented in SPIR-V
747 // E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be.
748 bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; }
750 // get the direct pointer for an l-value
751 Id accessChainGetLValue();
753 // Get the inferred SPIR-V type of the result of the current access chain,
754 // based on the type of the base and the chain of dereferences.
755 Id accessChainGetInferredType();
757 // Add capabilities, extensions, remove unneeded decorations, etc.,
758 // based on the resulting SPIR-V.
761 // Prune unreachable blocks in the CFG and remove unneeded decorations.
762 void postProcessCFG();
765 // Add capabilities, extensions based on instructions in the module.
766 void postProcessFeatures();
767 // Hook to visit each instruction in a block in a function
768 void postProcess(Instruction&);
769 // Hook to visit each non-32-bit sized float/int operation in a block.
770 void postProcessType(const Instruction&, spv::Id typeId);
773 void dump(std::vector<unsigned int>&) const;
775 void createBranch(Block* block);
776 void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
777 void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
778 const std::vector<unsigned int>& operands);
779 void createAndSetNoPredecessorBlock(const char*);
781 // Sets to generate opcode for specialization constants.
782 void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }
783 // Sets to generate opcode for non-specialization constants (normal mode).
784 void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; }
785 // Check if the builder is generating code for spec constants.
786 bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; }
789 Id makeIntConstant(Id typeId, unsigned value, bool specConstant);
790 Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant);
791 Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value);
792 Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2);
793 Id findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps);
794 Id findStructConstant(Id typeId, const std::vector<Id>& comps);
795 Id collapseAccessChain();
796 void remapDynamicSwizzle();
797 void transferAccessChainSwizzle(bool dynamic);
798 void simplifyAccessChainSwizzle();
799 void createSelectionMerge(Block* mergeBlock, unsigned int control);
800 void dumpSourceInstructions(std::vector<unsigned int>&) const;
801 void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const;
802 void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const;
803 void dumpModuleProcesses(std::vector<unsigned int>&) const;
804 spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)
807 unsigned int spvVersion; // the version of SPIR-V to emit in the header
808 SourceLanguage source;
810 spv::Id sourceFileStringId;
811 std::string sourceText;
813 const char* currentFile;
815 std::set<std::string> extensions;
816 std::vector<const char*> sourceExtensions;
817 std::vector<const char*> moduleProcesses;
818 AddressingModel addressModel;
819 MemoryModel memoryModel;
820 std::set<spv::Capability> capabilities;
825 Function* entryPointFunction;
826 bool generatingOpCodeForSpecConst;
827 AccessChain accessChain;
829 // special blocks of instructions for output
830 std::vector<std::unique_ptr<Instruction> > strings;
831 std::vector<std::unique_ptr<Instruction> > imports;
832 std::vector<std::unique_ptr<Instruction> > entryPoints;
833 std::vector<std::unique_ptr<Instruction> > executionModes;
834 std::vector<std::unique_ptr<Instruction> > names;
835 std::vector<std::unique_ptr<Instruction> > decorations;
836 std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals;
837 std::vector<std::unique_ptr<Instruction> > externals;
838 std::vector<std::unique_ptr<Function> > functions;
840 // not output, internally used for quick & dirty canonical (unique) creation
842 // map type opcodes to constant inst.
843 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants;
844 // map struct-id to constant instructions
845 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants;
846 // map type opcodes to type instructions
847 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes;
848 // list of OpConstantNull instructions
849 std::vector<Instruction*> nullConstants;
852 std::stack<Block*> switchMerges;
855 std::stack<LoopBlocks> loops;
857 // map from strings to their string ids
858 std::unordered_map<std::string, spv::Id> stringIds;
860 // map from include file name ids to their contents
861 std::map<spv::Id, const std::string*> includeFiles;
863 // The stream for outputting warnings and errors.
864 SpvBuildLogger* logger;
865 }; // end Builder class
867 }; // end spv namespace
869 #endif // SpvBuilder_H