blob: b4417df81c5e4be8a0a6310f29337ef381fd414a [file] [log] [blame]
John Kessenich0df0cde2015-03-03 17:09:43 +00001//
2//Copyright (C) 2014 LunarG, Inc.
3//
4//All rights reserved.
5//
6//Redistribution and use in source and binary forms, with or without
7//modification, are permitted provided that the following conditions
8//are met:
9//
10// Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12//
13// Redistributions in binary form must reproduce the above
14// copyright notice, this list of conditions and the following
15// disclaimer in the documentation and/or other materials provided
16// with the distribution.
17//
18// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
19// contributors may be used to endorse or promote products derived
20// from this software without specific prior written permission.
21//
22//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33//POSSIBILITY OF SUCH DAMAGE.
34
35//
36// Author: John Kessenich, LunarG
37//
38
39//
40// "Builder" is an interface to fully build SPIR-V IR. Allocate one of
41// these to build (a thread safe) internal SPIR-V representation (IR),
42// and then dump it as a binary stream according to the SPIR-V specification.
43//
44// A Builder has a 1:1 relationship with a SPIR-V module.
45//
46
47#pragma once
48#ifndef SpvBuilder_H
49#define SpvBuilder_H
50
51#include "spirv.h"
52#include "spvIR.h"
53
54#include <algorithm>
55#include <stack>
56#include <map>
57
58namespace spv {
59
60class Builder {
61public:
62 Builder(unsigned int userNumber);
63 virtual ~Builder();
64
65 static const int maxMatrixSize = 4;
66
67 void setSource(spv::SourceLanguage lang, int version)
68 {
69 source = lang;
70 sourceVersion = version;
71 }
72 void addSourceExtension(const char* ext) { extensions.push_back(ext); }
73 Id import(const char*);
74 void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem)
75 {
76 addressModel = addr;
77 memoryModel = mem;
78 }
79
80 // To get a new <id> for anything needing a new one.
81 Id getUniqueId() { return ++uniqueId; }
82
83 // To get a set of new <id>s, e.g., for a set of function parameters
84 Id getUniqueIds(int numIds)
85 {
86 Id id = uniqueId + 1;
87 uniqueId += numIds;
88 return id;
89 }
90
91 // For creating new types (will return old type if the requested one was already made).
92 Id makeVoidType();
93 Id makeBoolType();
94 Id makePointer(StorageClass, Id type);
95 Id makeIntegerType(int width, bool hasSign); // generic
96 Id makeIntType(int width) { return makeIntegerType(width, true); }
97 Id makeUintType(int width) { return makeIntegerType(width, false); }
98 Id makeFloatType(int width);
99 Id makeStructType(std::vector<Id>& members, const char*);
100 Id makeVectorType(Id component, int size);
101 Id makeMatrixType(Id component, int cols, int rows);
102 Id makeArrayType(Id element, unsigned size);
103 Id makeFunctionType(Id returnType, std::vector<Id>& paramTypes);
104 enum samplerContent {
105 samplerContentTexture,
106 samplerContentImage,
107 samplerContentTextureFilter
108 };
John Kessenichb40d6ac2015-03-30 17:41:16 +0000109 Id makeSampler(Id sampledType, Dim, samplerContent, bool arrayed, bool shadow, bool ms);
John Kessenich0df0cde2015-03-03 17:09:43 +0000110
111 // For querying about types.
112 Id getTypeId(Id resultId) const { return module.getTypeId(resultId); }
113 Id getDerefTypeId(Id resultId) const;
John Kessenichb40d6ac2015-03-30 17:41:16 +0000114 Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); }
115 Op getTypeClass(Id typeId) const { return getOpCode(typeId); }
116 Op getMostBasicTypeClass(Id typeId) const;
John Kessenich0df0cde2015-03-03 17:09:43 +0000117 int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); }
118 int getNumTypeComponents(Id typeId) const;
119 Id getScalarTypeId(Id typeId) const;
120 Id getContainedTypeId(Id typeId) const;
121 Id getContainedTypeId(Id typeId, int) const;
122
123 bool isPointer(Id resultId) const { return isPointerType(getTypeId(resultId)); }
124 bool isScalar(Id resultId) const { return isScalarType(getTypeId(resultId)); }
125 bool isVector(Id resultId) const { return isVectorType(getTypeId(resultId)); }
126 bool isMatrix(Id resultId) const { return isMatrixType(getTypeId(resultId)); }
127 bool isAggregate(Id resultId) const { return isAggregateType(getTypeId(resultId)); }
128
129 bool isPointerType(Id typeId) const { return getTypeClass(typeId) == OpTypePointer; }
130 bool isScalarType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt || getTypeClass(typeId) == OpTypeBool; }
131 bool isVectorType(Id typeId) const { return getTypeClass(typeId) == OpTypeVector; }
132 bool isMatrixType(Id typeId) const { return getTypeClass(typeId) == OpTypeMatrix; }
133 bool isStructType(Id typeId) const { return getTypeClass(typeId) == OpTypeStruct; }
134 bool isArrayType(Id typeId) const { return getTypeClass(typeId) == OpTypeArray; }
135 bool isAggregateType(Id typeId) const { return isArrayType(typeId) || isStructType(typeId); }
136 bool isSamplerType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampler; }
137
138 bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }
139 unsigned int getConstantScalar(Id resultId) const { return module.getInstruction(resultId)->getImmediateOperand(0); }
140
141 int getTypeNumColumns(Id typeId) const
142 {
143 assert(isMatrixType(typeId));
144 return getNumTypeComponents(typeId);
145 }
146 int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); }
147 int getTypeNumRows(Id typeId) const
148 {
149 assert(isMatrixType(typeId));
150 return getNumTypeComponents(getContainedTypeId(typeId));
151 }
152 int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); }
153
John Kessenichb40d6ac2015-03-30 17:41:16 +0000154 Dim getDimensionality(Id resultId) const
John Kessenich0df0cde2015-03-03 17:09:43 +0000155 {
156 assert(isSamplerType(getTypeId(resultId)));
John Kessenichb40d6ac2015-03-30 17:41:16 +0000157 return (Dim)module.getInstruction(getTypeId(resultId))->getImmediateOperand(1);
John Kessenich0df0cde2015-03-03 17:09:43 +0000158 }
159 bool isArrayedSampler(Id resultId) const
160 {
161 assert(isSamplerType(getTypeId(resultId)));
162 return module.getInstruction(getTypeId(resultId))->getImmediateOperand(3) != 0;
163 }
164
165 // For making new constants (will return old constant if the requested one was already made).
166 Id makeBoolConstant(bool b);
167 Id makeIntConstant(Id typeId, unsigned value);
168 Id makeIntConstant(int i) { return makeIntConstant(makeIntType(32), (unsigned)i); }
169 Id makeUintConstant(unsigned u) { return makeIntConstant(makeUintType(32), u); }
170 Id makeFloatConstant(float f);
171 Id makeDoubleConstant(double d);
172
173 // Turn the array of constants into a proper spv constant of the requested type.
174 Id makeCompositeConstant(Id type, std::vector<Id>& comps);
175
176 // Methods for adding information outside the CFG.
177 void addEntryPoint(ExecutionModel, Function*);
178 void addExecutionMode(Function*, ExecutionMode mode, int value = -1);
179 void addName(Id, const char* name);
180 void addMemberName(Id, int member, const char* name);
181 void addLine(Id target, Id fileName, int line, int column);
182 void addDecoration(Id, Decoration, int num = -1);
183 void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
184
185 // At the end of what block do the next create*() instructions go?
186 void setBuildPoint(Block* bp) { buildPoint = bp; }
187 Block* getBuildPoint() const { return buildPoint; }
188
189 // Make the main function.
190 Function* makeMain();
191
192 // Return from main. Implicit denotes a return at the very end of main.
193 void makeMainReturn(bool implicit = false) { makeReturn(implicit, 0, true); }
194
195 // Close the main function.
196 void closeMain();
197
198 // Make a shader-style function, and create its entry block if entry is non-zero.
199 // Return the function, pass back the entry.
200 Function* makeFunctionEntry(Id returnType, const char* name, std::vector<Id>& paramTypes, Block **entry = 0);
201
202 // Create a return. Pass whether it is a return form main, and the return
203 // value (if applicable). In the case of an implicit return, no post-return
204 // block is inserted.
205 void makeReturn(bool implicit = false, Id retVal = 0, bool isMain = false);
206
207 // Generate all the code needed to finish up a function.
208 void leaveFunction(bool main);
209
210 // Create a discard.
211 void makeDiscard();
212
213 // Create a global or function local or IO variable.
214 Id createVariable(StorageClass, Id type, const char* name = 0);
215
216 // Store into an Id and return the l-value
217 void createStore(Id rValue, Id lValue);
218
219 // Load from an Id and return it
220 Id createLoad(Id lValue);
221
222 // Create an OpAccessChain instruction
223 Id createAccessChain(StorageClass, Id base, std::vector<Id>& offsets);
224
225 // Create an OpCompositeExtract instruction
226 Id createCompositeExtract(Id composite, Id typeId, unsigned index);
227 Id createCompositeExtract(Id composite, Id typeId, std::vector<unsigned>& indexes);
228 Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
229 Id createCompositeInsert(Id object, Id composite, Id typeId, std::vector<unsigned>& indexes);
230
John Kessenichb40d6ac2015-03-30 17:41:16 +0000231 void createNoResultOp(Op);
232 void createNoResultOp(Op, Id operand);
John Kessenich0df0cde2015-03-03 17:09:43 +0000233 void createControlBarrier(unsigned executionScope);
234 void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
John Kessenichb40d6ac2015-03-30 17:41:16 +0000235 Id createUnaryOp(Op, Id typeId, Id operand);
236 Id createBinOp(Op, Id typeId, Id operand1, Id operand2);
237 Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
238 Id createTernaryOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
John Kessenich0df0cde2015-03-03 17:09:43 +0000239 Id createFunctionCall(spv::Function*, std::vector<spv::Id>&);
240
241 // Take an rvalue (source) and a set of channels to extract from it to
242 // make a new rvalue, which is returned.
243 Id createRvalueSwizzle(Id typeId, Id source, std::vector<unsigned>& channels);
244
245 // Take a copy of an lvalue (target) and a source of components, and set the
246 // source components into the lvalue where the 'channels' say to put them.
247 // An update version of the target is returned.
248 // (No true lvalue or stores are used.)
249 Id createLvalueSwizzle(Id typeId, Id target, Id source, std::vector<unsigned>& channels);
250
251 // If the value passed in is an instruction and the precision is not EMpNone,
252 // it gets tagged with the requested precision.
253 void setPrecision(Id value, Decoration precision)
254 {
255 // TODO
256 }
257
258 // Can smear a scalar to a vector for the following forms:
259 // - promoteScalar(scalar, vector) // smear scalar to width of vector
260 // - promoteScalar(vector, scalar) // smear scalar to width of vector
261 // - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to
262 // - promoteScalar(scalar, scalar) // do nothing
263 // Other forms are not allowed.
264 //
265 // Note: One of the arguments will change, with the result coming back that way rather than
266 // through the return value.
267 void promoteScalar(Decoration precision, Id& left, Id& right);
268
269 // make a value by smearing the scalar to fill the type
270 Id smearScalar(Decoration precision, Id scalarVal, Id);
271
272 // Create a call to a built-in function.
273 Id createBuiltinCall(Decoration precision, Id resultType, Id builtins, int entryPoint, std::vector<Id>& args);
274
275 // List of parameters used to create a texture operation
276 struct TextureParameters {
277 Id sampler;
278 Id coords;
279 Id bias;
280 Id lod;
281 Id Dref;
282 Id offset;
283 Id gradX;
284 Id gradY;
285 };
286
287 // Select the correct texture operation based on all inputs, and emit the correct instruction
288 Id createTextureCall(Decoration precision, Id resultType, bool proj, const TextureParameters&);
289
290 // Emit the OpTextureQuery* instruction that was passed in.
291 // Figure out the right return value and type, and return it.
John Kessenichb40d6ac2015-03-30 17:41:16 +0000292 Id createTextureQueryCall(Op, const TextureParameters&);
John Kessenich0df0cde2015-03-03 17:09:43 +0000293
294 Id createSamplePositionCall(Decoration precision, Id, Id);
295
296 Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned);
297 Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id);
298
299 // Reduction comparision for composites: For equal and not-equal resulting in a scalar.
300 Id createCompare(Decoration precision, Id, Id, bool /* true if for equal, fales if for not-equal */);
301
302 // OpCompositeConstruct
303 Id createCompositeConstruct(Id typeId, std::vector<Id>& constituents);
304
305 // vector or scalar constructor
306 Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId);
307
308 // matrix constructor
309 Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee);
310
311 // Helper to use for building nested control flow with if-then-else.
312 class If {
313 public:
314 If(Id condition, Builder& builder);
315 ~If() {}
316
317 void makeBeginElse();
318 void makeEndIf();
319
320 private:
321 Builder& builder;
322 Id condition;
323 Function* function;
324 Block* headerBlock;
325 Block* thenBlock;
326 Block* elseBlock;
327 Block* mergeBlock;
328 };
329
330 // Make a switch statement. A switch has 'numSegments' of pieces of code, not containing
331 // any case/default labels, all separated by one or more case/default labels. Each possible
332 // case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this
333 // number space. How to compute the value is given by 'condition', as in switch(condition).
334 //
335 // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.
336 //
337 // Use a defaultSegment < 0 if there is no default segment (to branch to post switch).
338 //
339 // Returns the right set of basic blocks to start each code segment with, so that the caller's
340 // recursion stack can hold the memory for it.
341 //
342 void makeSwitch(Id condition, int numSegments, std::vector<int>& caseValues, std::vector<int>& valueToSegment, int defaultSegment,
343 std::vector<Block*>& segmentBB); // return argument
344
345 // Add a branch to the innermost switch's merge block.
346 void addSwitchBreak();
347
348 // Move to the next code segment, passing in the return argument in makeSwitch()
349 void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment);
350
351 // Finish off the innermost switch.
352 void endSwitch(std::vector<Block*>& segmentBB);
353
354 // Start the beginning of a new loop.
355 void makeNewLoop();
356
357 // Add the branch at the end of the loop header, and leave the build position
358 // in the first block of the body.
359 // 'condition' is true if should exit the loop
360 void createLoopHeaderBranch(Id condition);
361
362 // Add a back-edge (e.g "continue") for the innermost loop that you're in
363 void createLoopBackEdge(bool implicit=false);
364
365 // Add an exit (e.g. "break") for the innermost loop that you're in
366 void createLoopExit();
367
368 // Close the innermost loop that you're in
369 void closeLoop();
370
371 //
372 // Access chain design for an R-Value vs. L-Value:
373 //
374 // There is a single access chain the builder is building at
375 // any particular time. Such a chain can be used to either to a load or
376 // a store, when desired.
377 //
378 // Expressions can be r-values, l-values, or both, or only r-values:
379 // a[b.c].d = .... // l-value
380 // ... = a[b.c].d; // r-value, that also looks like an l-value
381 // ++a[b.c].d; // r-value and l-value
382 // (x + y)[2]; // r-value only, can't possibly be l-value
383 //
384 // Computing an r-value means generating code. Hence,
385 // r-values should only be computed when they are needed, not speculatively.
386 //
387 // Computing an l-value means saving away information for later use in the compiler,
388 // no code is generated until the l-value is later dereferenced. It is okay
389 // to speculatively generate an l-value, just not okay to speculatively dereference it.
390 //
391 // The base of the access chain (the left-most variable or expression
392 // from which everything is based) can be set either as an l-value
393 // or as an r-value. Most efficient would be to set an l-value if one
394 // is available. If an expression was evaluated, the resulting r-value
395 // can be set as the chain base.
396 //
397 // The users of this single access chain can save and restore if they
398 // want to nest or manage multiple chains.
399 //
400
401 struct AccessChain {
402 Id base; // for l-values, pointer to the base object, for r-values, the base object
403 std::vector<Id> indexChain;
404 Id instr; // the instruction that generates this access chain
405 std::vector<unsigned> swizzle;
406 Id component; // a dynamic component index
407 int swizzleTargetWidth;
408 Id resultType; // dereferenced type, to be inclusive of swizzles, which can't have a pointer
409 bool isRValue;
410 };
411
412 //
413 // the SPIR-V builder maintains a single active chain that
414 // the following methods operated on
415 //
416
417 // for external save and restore
418 AccessChain getAccessChain() { return accessChain; }
419 void setAccessChain(AccessChain newChain) { accessChain = newChain; }
420
421 // clear accessChain
422 void clearAccessChain();
423
424 // set new base as an l-value base
425 void setAccessChainLValue(Id lValue)
426 {
427 assert(isPointer(lValue));
428 accessChain.base = lValue;
429 }
430
431 // set new base value as an r-value
432 void setAccessChainRValue(Id rValue)
433 {
434 accessChain.isRValue = true;
435 accessChain.base = rValue;
436 accessChain.resultType = getTypeId(rValue);
437 }
438
439 // push offset onto the end of the chain
440 void accessChainPush(Id offset, Id newType)
441 {
442 accessChain.indexChain.push_back(offset);
443 accessChain.resultType = newType;
444 }
445
446 // push new swizzle onto the end of any existing swizzle, merging into a single swizzle
447 void accessChainPushSwizzle(std::vector<unsigned>& swizzle, int width, Id type);
448
449 // push a variable component selection onto the access chain; supporting only one, so unsided
450 void accessChainPushComponent(Id component) { accessChain.component = component; }
451
452 // use accessChain and swizzle to store value
453 void accessChainStore(Id rvalue);
454
455 // use accessChain and swizzle to load an r-value
456 Id accessChainLoad(Decoration precision);
457
458 // get the direct pointer for an l-value
459 Id accessChainGetLValue();
460
461 void dump(std::vector<unsigned int>&) const;
462
463protected:
John Kessenichb40d6ac2015-03-30 17:41:16 +0000464 Id findScalarConstant(Op typeClass, Id typeId, unsigned value) const;
John Kessenichedd18192015-04-17 21:47:07 +0000465 Id findScalarConstant(Op typeClass, Id typeId, unsigned v1, unsigned v2) const;
John Kessenichb40d6ac2015-03-30 17:41:16 +0000466 Id findCompositeConstant(Op typeClass, std::vector<Id>& comps) const;
John Kessenich0df0cde2015-03-03 17:09:43 +0000467 Id collapseAccessChain();
468 void simplifyAccessChainSwizzle();
469 void createAndSetNoPredecessorBlock(const char*);
470 void createBranch(Block* block);
John Kessenichb40d6ac2015-03-30 17:41:16 +0000471 void createMerge(Op, Block*, unsigned int control);
John Kessenich0df0cde2015-03-03 17:09:43 +0000472 void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
473 void dumpInstructions(std::vector<unsigned int>&, const std::vector<Instruction*>&) const;
474
475 SourceLanguage source;
476 int sourceVersion;
477 std::vector<const char*> extensions;
478 AddressingModel addressModel;
479 MemoryModel memoryModel;
480 int builderNumber;
481 Module module;
482 Block* buildPoint;
483 Id uniqueId;
484 Function* mainFunction;
485 Block* stageExit;
486 AccessChain accessChain;
487
488 // special blocks of instructions for output
489 std::vector<Instruction*> imports;
490 std::vector<Instruction*> entryPoints;
491 std::vector<Instruction*> executionModes;
492 std::vector<Instruction*> names;
493 std::vector<Instruction*> lines;
494 std::vector<Instruction*> decorations;
495 std::vector<Instruction*> constantsTypesGlobals;
496 std::vector<Instruction*> externals;
497
498 // not output, internally used for quick & dirty canonical (unique) creation
499 std::vector<Instruction*> groupedConstants[OpConstant]; // all types appear before OpConstant
500 std::vector<Instruction*> groupedTypes[OpConstant];
501
502 // stack of switches
503 std::stack<Block*> switchMerges;
504
505 // Data that needs to be kept in order to properly handle loops.
506 struct Loop {
507 Block* header;
508 Block* merge;
509 Function* function;
510 };
511
512 // Our loop stack.
513 std::stack<Loop> loops;
514}; // end Builder class
515
516void MissingFunctionality(const char*);
517void ValidationError(const char* error);
518
519}; // end spv namespace
520
521#endif // SpvBuilder_H