123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866 |
- //
- // Copyright (C) 2014-2015 LunarG, Inc.
- // Copyright (C) 2015-2020 Google, Inc.
- // Copyright (C) 2017 ARM Limited.
- // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
- //
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions
- // are met:
- //
- // Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- //
- // Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following
- // disclaimer in the documentation and/or other materials provided
- // with the distribution.
- //
- // Neither the name of 3Dlabs Inc. Ltd. nor the names of its
- // contributors may be used to endorse or promote products derived
- // from this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- // POSSIBILITY OF SUCH DAMAGE.
- //
- // "Builder" is an interface to fully build SPIR-V IR. Allocate one of
- // these to build (a thread safe) internal SPIR-V representation (IR),
- // and then dump it as a binary stream according to the SPIR-V specification.
- //
- // A Builder has a 1:1 relationship with a SPIR-V module.
- //
- #pragma once
- #ifndef SpvBuilder_H
- #define SpvBuilder_H
- #include "Logger.h"
- #include "spirv.hpp"
- #include "spvIR.h"
- #include <algorithm>
- #include <map>
- #include <memory>
- #include <set>
- #include <sstream>
- #include <stack>
- #include <unordered_map>
- #include <map>
- namespace spv {
- typedef enum {
- Spv_1_0 = (1 << 16),
- Spv_1_1 = (1 << 16) | (1 << 8),
- Spv_1_2 = (1 << 16) | (2 << 8),
- Spv_1_3 = (1 << 16) | (3 << 8),
- Spv_1_4 = (1 << 16) | (4 << 8),
- Spv_1_5 = (1 << 16) | (5 << 8),
- } SpvVersion;
- class Builder {
- public:
- Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger);
- virtual ~Builder();
- static const int maxMatrixSize = 4;
- unsigned int getSpvVersion() const { return spvVersion; }
- void setSource(spv::SourceLanguage lang, int version)
- {
- source = lang;
- sourceVersion = version;
- }
- spv::Id getStringId(const std::string& str)
- {
- auto sItr = stringIds.find(str);
- if (sItr != stringIds.end())
- return sItr->second;
- spv::Id strId = getUniqueId();
- Instruction* fileString = new Instruction(strId, NoType, OpString);
- const char* file_c_str = str.c_str();
- fileString->addStringOperand(file_c_str);
- strings.push_back(std::unique_ptr<Instruction>(fileString));
- module.mapInstruction(fileString);
- stringIds[file_c_str] = strId;
- return strId;
- }
- void setSourceFile(const std::string& file)
- {
- sourceFileStringId = getStringId(file);
- }
- void setSourceText(const std::string& text) { sourceText = text; }
- void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); }
- void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); }
- void setEmitOpLines() { emitOpLines = true; }
- void addExtension(const char* ext) { extensions.insert(ext); }
- void removeExtension(const char* ext)
- {
- extensions.erase(ext);
- }
- void addIncorporatedExtension(const char* ext, SpvVersion incorporatedVersion)
- {
- if (getSpvVersion() < static_cast<unsigned>(incorporatedVersion))
- addExtension(ext);
- }
- void promoteIncorporatedExtension(const char* baseExt, const char* promoExt, SpvVersion incorporatedVersion)
- {
- removeExtension(baseExt);
- addIncorporatedExtension(promoExt, incorporatedVersion);
- }
- void addInclude(const std::string& name, const std::string& text)
- {
- spv::Id incId = getStringId(name);
- includeFiles[incId] = &text;
- }
- Id import(const char*);
- void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem)
- {
- addressModel = addr;
- memoryModel = mem;
- }
- void addCapability(spv::Capability cap) { capabilities.insert(cap); }
- // To get a new <id> for anything needing a new one.
- Id getUniqueId() { return ++uniqueId; }
- // To get a set of new <id>s, e.g., for a set of function parameters
- Id getUniqueIds(int numIds)
- {
- Id id = uniqueId + 1;
- uniqueId += numIds;
- return id;
- }
- // Generate OpLine for non-filename-based #line directives (ie no filename
- // seen yet): Log the current line, and if different than the last one,
- // issue a new OpLine using the new line and current source file name.
- void setLine(int line);
- // If filename null, generate OpLine for non-filename-based line directives,
- // else do filename-based: Log the current line and file, and if different
- // than the last one, issue a new OpLine using the new line and file
- // name.
- void setLine(int line, const char* filename);
- // Low-level OpLine. See setLine() for a layered helper.
- void addLine(Id fileName, int line, int column);
- // For creating new types (will return old type if the requested one was already made).
- Id makeVoidType();
- Id makeBoolType();
- Id makePointer(StorageClass, Id pointee);
- Id makeForwardPointer(StorageClass);
- Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee);
- Id makeIntegerType(int width, bool hasSign); // generic
- Id makeIntType(int width) { return makeIntegerType(width, true); }
- Id makeUintType(int width) { return makeIntegerType(width, false); }
- Id makeFloatType(int width);
- Id makeStructType(const std::vector<Id>& members, const char*);
- Id makeStructResultType(Id type0, Id type1);
- Id makeVectorType(Id component, int size);
- Id makeMatrixType(Id component, int cols, int rows);
- Id makeArrayType(Id element, Id sizeId, int stride); // 0 stride means no stride decoration
- Id makeRuntimeArray(Id element);
- Id makeFunctionType(Id returnType, const std::vector<Id>& paramTypes);
- Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format);
- Id makeSamplerType();
- Id makeSampledImageType(Id imageType);
- Id makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols);
- Id makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands);
- // accelerationStructureNV type
- Id makeAccelerationStructureType();
- // rayQueryEXT type
- Id makeRayQueryType();
- // For querying about types.
- Id getTypeId(Id resultId) const { return module.getTypeId(resultId); }
- Id getDerefTypeId(Id resultId) const;
- Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); }
- Op getTypeClass(Id typeId) const { return getOpCode(typeId); }
- Op getMostBasicTypeClass(Id typeId) const;
- int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); }
- int getNumTypeConstituents(Id typeId) const;
- int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); }
- Id getScalarTypeId(Id typeId) const;
- Id getContainedTypeId(Id typeId) const;
- Id getContainedTypeId(Id typeId, int) const;
- StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); }
- ImageFormat getImageTypeFormat(Id typeId) const
- { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); }
- Id getResultingAccessChainType() const;
- bool isPointer(Id resultId) const { return isPointerType(getTypeId(resultId)); }
- bool isScalar(Id resultId) const { return isScalarType(getTypeId(resultId)); }
- bool isVector(Id resultId) const { return isVectorType(getTypeId(resultId)); }
- bool isMatrix(Id resultId) const { return isMatrixType(getTypeId(resultId)); }
- bool isCooperativeMatrix(Id resultId)const { return isCooperativeMatrixType(getTypeId(resultId)); }
- bool isAggregate(Id resultId) const { return isAggregateType(getTypeId(resultId)); }
- bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); }
- bool isBoolType(Id typeId)
- { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); }
- bool isIntType(Id typeId) const
- { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; }
- bool isUintType(Id typeId) const
- { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; }
- bool isFloatType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat; }
- bool isPointerType(Id typeId) const { return getTypeClass(typeId) == OpTypePointer; }
- bool isScalarType(Id typeId) const
- { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt ||
- getTypeClass(typeId) == OpTypeBool; }
- bool isVectorType(Id typeId) const { return getTypeClass(typeId) == OpTypeVector; }
- bool isMatrixType(Id typeId) const { return getTypeClass(typeId) == OpTypeMatrix; }
- bool isStructType(Id typeId) const { return getTypeClass(typeId) == OpTypeStruct; }
- bool isArrayType(Id typeId) const { return getTypeClass(typeId) == OpTypeArray; }
- #ifdef GLSLANG_WEB
- bool isCooperativeMatrixType(Id typeId)const { return false; }
- #else
- bool isCooperativeMatrixType(Id typeId)const { return getTypeClass(typeId) == OpTypeCooperativeMatrixNV; }
- #endif
- bool isAggregateType(Id typeId) const
- { return isArrayType(typeId) || isStructType(typeId) || isCooperativeMatrixType(typeId); }
- bool isImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeImage; }
- bool isSamplerType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampler; }
- bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; }
- bool containsType(Id typeId, Op typeOp, unsigned int width) const;
- bool containsPhysicalStorageBufferOrArray(Id typeId) const;
- bool isConstantOpCode(Op opcode) const;
- bool isSpecConstantOpCode(Op opcode) const;
- bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); }
- bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }
- bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); }
- unsigned int getConstantScalar(Id resultId) const
- { return module.getInstruction(resultId)->getImmediateOperand(0); }
- StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); }
- bool isVariableOpCode(Op opcode) const { return opcode == OpVariable; }
- bool isVariable(Id resultId) const { return isVariableOpCode(getOpCode(resultId)); }
- bool isGlobalStorage(Id resultId) const { return getStorageClass(resultId) != StorageClassFunction; }
- bool isGlobalVariable(Id resultId) const { return isVariable(resultId) && isGlobalStorage(resultId); }
- // See if a resultId is valid for use as an initializer.
- bool isValidInitializer(Id resultId) const { return isConstant(resultId) || isGlobalVariable(resultId); }
- int getScalarTypeWidth(Id typeId) const
- {
- Id scalarTypeId = getScalarTypeId(typeId);
- assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat);
- return module.getInstruction(scalarTypeId)->getImmediateOperand(0);
- }
- int getTypeNumColumns(Id typeId) const
- {
- assert(isMatrixType(typeId));
- return getNumTypeConstituents(typeId);
- }
- int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); }
- int getTypeNumRows(Id typeId) const
- {
- assert(isMatrixType(typeId));
- return getNumTypeComponents(getContainedTypeId(typeId));
- }
- int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); }
- Dim getTypeDimensionality(Id typeId) const
- {
- assert(isImageType(typeId));
- return (Dim)module.getInstruction(typeId)->getImmediateOperand(1);
- }
- Id getImageType(Id resultId) const
- {
- Id typeId = getTypeId(resultId);
- assert(isImageType(typeId) || isSampledImageType(typeId));
- return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId;
- }
- bool isArrayedImageType(Id typeId) const
- {
- assert(isImageType(typeId));
- return module.getInstruction(typeId)->getImmediateOperand(3) != 0;
- }
- // For making new constants (will return old constant if the requested one was already made).
- Id makeNullConstant(Id typeId);
- Id makeBoolConstant(bool b, bool specConstant = false);
- Id makeInt8Constant(int i, bool specConstant = false)
- { return makeIntConstant(makeIntType(8), (unsigned)i, specConstant); }
- Id makeUint8Constant(unsigned u, bool specConstant = false)
- { return makeIntConstant(makeUintType(8), u, specConstant); }
- Id makeInt16Constant(int i, bool specConstant = false)
- { return makeIntConstant(makeIntType(16), (unsigned)i, specConstant); }
- Id makeUint16Constant(unsigned u, bool specConstant = false)
- { return makeIntConstant(makeUintType(16), u, specConstant); }
- Id makeIntConstant(int i, bool specConstant = false)
- { return makeIntConstant(makeIntType(32), (unsigned)i, specConstant); }
- Id makeUintConstant(unsigned u, bool specConstant = false)
- { return makeIntConstant(makeUintType(32), u, specConstant); }
- Id makeInt64Constant(long long i, bool specConstant = false)
- { return makeInt64Constant(makeIntType(64), (unsigned long long)i, specConstant); }
- Id makeUint64Constant(unsigned long long u, bool specConstant = false)
- { return makeInt64Constant(makeUintType(64), u, specConstant); }
- Id makeFloatConstant(float f, bool specConstant = false);
- Id makeDoubleConstant(double d, bool specConstant = false);
- Id makeFloat16Constant(float f16, bool specConstant = false);
- Id makeFpConstant(Id type, double d, bool specConstant = false);
- // Turn the array of constants into a proper spv constant of the requested type.
- Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false);
- // Methods for adding information outside the CFG.
- Instruction* addEntryPoint(ExecutionModel, Function*, const char* name);
- void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1);
- void addExecutionMode(Function*, ExecutionMode mode, const std::vector<unsigned>& literals);
- void addExecutionModeId(Function*, ExecutionMode mode, const std::vector<Id>& operandIds);
- void addName(Id, const char* name);
- void addMemberName(Id, int member, const char* name);
- void addDecoration(Id, Decoration, int num = -1);
- void addDecoration(Id, Decoration, const char*);
- void addDecoration(Id, Decoration, const std::vector<unsigned>& literals);
- void addDecoration(Id, Decoration, const std::vector<const char*>& strings);
- void addDecorationId(Id id, Decoration, Id idDecoration);
- void addDecorationId(Id id, Decoration, const std::vector<Id>& operandIds);
- void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
- void addMemberDecoration(Id, unsigned int member, Decoration, const char*);
- void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<unsigned>& literals);
- void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<const char*>& strings);
- // At the end of what block do the next create*() instructions go?
- void setBuildPoint(Block* bp) { buildPoint = bp; }
- Block* getBuildPoint() const { return buildPoint; }
- // Make the entry-point function. The returned pointer is only valid
- // for the lifetime of this builder.
- Function* makeEntryPoint(const char*);
- // Make a shader-style function, and create its entry block if entry is non-zero.
- // Return the function, pass back the entry.
- // The returned pointer is only valid for the lifetime of this builder.
- Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name,
- const std::vector<Id>& paramTypes, const std::vector<std::vector<Decoration>>& precisions, Block **entry = 0);
- // Create a return. An 'implicit' return is one not appearing in the source
- // code. In the case of an implicit return, no post-return block is inserted.
- void makeReturn(bool implicit, Id retVal = 0);
- // Generate all the code needed to finish up a function.
- void leaveFunction();
- // Create block terminator instruction for certain statements like
- // discard, terminate-invocation, terminateRayEXT, or ignoreIntersectionEXT
- void makeStatementTerminator(spv::Op opcode, const char *name);
- // Create a global or function local or IO variable.
- Id createVariable(Decoration precision, StorageClass, Id type, const char* name = nullptr,
- Id initializer = NoResult);
- // Create an intermediate with an undefined value.
- Id createUndefined(Id type);
- // Store into an Id and return the l-value
- void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
- spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
- // Load from an Id and return it
- Id createLoad(Id lValue, spv::Decoration precision,
- spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
- spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
- // Create an OpAccessChain instruction
- Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets);
- // Create an OpArrayLength instruction
- Id createArrayLength(Id base, unsigned int member);
- // Create an OpCooperativeMatrixLengthNV instruction
- Id createCooperativeMatrixLength(Id type);
- // Create an OpCompositeExtract instruction
- Id createCompositeExtract(Id composite, Id typeId, unsigned index);
- Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes);
- Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
- Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes);
- Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex);
- Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex);
- void createNoResultOp(Op);
- void createNoResultOp(Op, Id operand);
- void createNoResultOp(Op, const std::vector<Id>& operands);
- void createNoResultOp(Op, const std::vector<IdImmediate>& operands);
- void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask);
- void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
- Id createUnaryOp(Op, Id typeId, Id operand);
- Id createBinOp(Op, Id typeId, Id operand1, Id operand2);
- Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
- Id createOp(Op, Id typeId, const std::vector<Id>& operands);
- Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands);
- Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&);
- Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals);
- // Take an rvalue (source) and a set of channels to extract from it to
- // make a new rvalue, which is returned.
- Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels);
- // Take a copy of an lvalue (target) and a source of components, and set the
- // source components into the lvalue where the 'channels' say to put them.
- // An updated version of the target is returned.
- // (No true lvalue or stores are used.)
- Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels);
- // If both the id and precision are valid, the id
- // gets tagged with the requested precision.
- // The passed in id is always the returned id, to simplify use patterns.
- Id setPrecision(Id id, Decoration precision)
- {
- if (precision != NoPrecision && id != NoResult)
- addDecoration(id, precision);
- return id;
- }
- // Can smear a scalar to a vector for the following forms:
- // - promoteScalar(scalar, vector) // smear scalar to width of vector
- // - promoteScalar(vector, scalar) // smear scalar to width of vector
- // - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to
- // - promoteScalar(scalar, scalar) // do nothing
- // Other forms are not allowed.
- //
- // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'.
- // The type of the created vector is a vector of components of the same type as the scalar.
- //
- // Note: One of the arguments will change, with the result coming back that way rather than
- // through the return value.
- void promoteScalar(Decoration precision, Id& left, Id& right);
- // Make a value by smearing the scalar to fill the type.
- // vectorType should be the correct type for making a vector of scalarVal.
- // (No conversions are done.)
- Id smearScalar(Decoration precision, Id scalarVal, Id vectorType);
- // Create a call to a built-in function.
- Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args);
- // List of parameters used to create a texture operation
- struct TextureParameters {
- Id sampler;
- Id coords;
- Id bias;
- Id lod;
- Id Dref;
- Id offset;
- Id offsets;
- Id gradX;
- Id gradY;
- Id sample;
- Id component;
- Id texelOut;
- Id lodClamp;
- Id granularity;
- Id coarse;
- bool nonprivate;
- bool volatil;
- };
- // Select the correct texture operation based on all inputs, and emit the correct instruction
- Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
- bool noImplicit, const TextureParameters&, ImageOperandsMask);
- // Emit the OpTextureQuery* instruction that was passed in.
- // Figure out the right return value and type, and return it.
- Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult);
- Id createSamplePositionCall(Decoration precision, Id, Id);
- Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned);
- Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id);
- // Reduction comparison for composites: For equal and not-equal resulting in a scalar.
- Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */);
- // OpCompositeConstruct
- Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents);
- // vector or scalar constructor
- Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId);
- // matrix constructor
- Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee);
- // Helper to use for building nested control flow with if-then-else.
- class If {
- public:
- If(Id condition, unsigned int ctrl, Builder& builder);
- ~If() {}
- void makeBeginElse();
- void makeEndIf();
- private:
- If(const If&);
- If& operator=(If&);
- Builder& builder;
- Id condition;
- unsigned int control;
- Function* function;
- Block* headerBlock;
- Block* thenBlock;
- Block* elseBlock;
- Block* mergeBlock;
- };
- // Make a switch statement. A switch has 'numSegments' of pieces of code, not containing
- // any case/default labels, all separated by one or more case/default labels. Each possible
- // case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this
- // number space. How to compute the value is given by 'condition', as in switch(condition).
- //
- // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.
- //
- // Use a defaultSegment < 0 if there is no default segment (to branch to post switch).
- //
- // Returns the right set of basic blocks to start each code segment with, so that the caller's
- // recursion stack can hold the memory for it.
- //
- void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector<int>& caseValues,
- const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB);
- // Add a branch to the innermost switch's merge block.
- void addSwitchBreak();
- // Move to the next code segment, passing in the return argument in makeSwitch()
- void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment);
- // Finish off the innermost switch.
- void endSwitch(std::vector<Block*>& segmentBB);
- struct LoopBlocks {
- LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) :
- head(head), body(body), merge(merge), continue_target(continue_target) { }
- Block &head, &body, &merge, &continue_target;
- private:
- LoopBlocks();
- LoopBlocks& operator=(const LoopBlocks&) = delete;
- };
- // Start a new loop and prepare the builder to generate code for it. Until
- // closeLoop() is called for this loop, createLoopContinue() and
- // createLoopExit() will target its corresponding blocks.
- LoopBlocks& makeNewLoop();
- // Create a new block in the function containing the build point. Memory is
- // owned by the function object.
- Block& makeNewBlock();
- // Add a branch to the continue_target of the current (innermost) loop.
- void createLoopContinue();
- // Add an exit (e.g. "break") from the innermost loop that we're currently
- // in.
- void createLoopExit();
- // Close the innermost loop that you're in
- void closeLoop();
- //
- // Access chain design for an R-Value vs. L-Value:
- //
- // There is a single access chain the builder is building at
- // any particular time. Such a chain can be used to either to a load or
- // a store, when desired.
- //
- // Expressions can be r-values, l-values, or both, or only r-values:
- // a[b.c].d = .... // l-value
- // ... = a[b.c].d; // r-value, that also looks like an l-value
- // ++a[b.c].d; // r-value and l-value
- // (x + y)[2]; // r-value only, can't possibly be l-value
- //
- // Computing an r-value means generating code. Hence,
- // r-values should only be computed when they are needed, not speculatively.
- //
- // Computing an l-value means saving away information for later use in the compiler,
- // no code is generated until the l-value is later dereferenced. It is okay
- // to speculatively generate an l-value, just not okay to speculatively dereference it.
- //
- // The base of the access chain (the left-most variable or expression
- // from which everything is based) can be set either as an l-value
- // or as an r-value. Most efficient would be to set an l-value if one
- // is available. If an expression was evaluated, the resulting r-value
- // can be set as the chain base.
- //
- // The users of this single access chain can save and restore if they
- // want to nest or manage multiple chains.
- //
- struct AccessChain {
- Id base; // for l-values, pointer to the base object, for r-values, the base object
- std::vector<Id> indexChain;
- Id instr; // cache the instruction that generates this access chain
- std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number
- Id component; // a dynamic component index, can coexist with a swizzle,
- // done after the swizzle, NoResult if not present
- Id preSwizzleBaseType; // dereferenced type, before swizzle or component is applied;
- // NoType unless a swizzle or component is present
- bool isRValue; // true if 'base' is an r-value, otherwise, base is an l-value
- unsigned int alignment; // bitwise OR of alignment values passed in. Accumulates worst alignment.
- // Only tracks base and (optional) component selection alignment.
- // Accumulate whether anything in the chain of structures has coherent decorations.
- struct CoherentFlags {
- CoherentFlags() { clear(); }
- #ifdef GLSLANG_WEB
- void clear() { }
- bool isVolatile() const { return false; }
- CoherentFlags operator |=(const CoherentFlags &other) { return *this; }
- #else
- bool isVolatile() const { return volatil; }
- bool isNonUniform() const { return nonUniform; }
- bool anyCoherent() const {
- return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent ||
- subgroupcoherent || shadercallcoherent;
- }
- unsigned coherent : 1;
- unsigned devicecoherent : 1;
- unsigned queuefamilycoherent : 1;
- unsigned workgroupcoherent : 1;
- unsigned subgroupcoherent : 1;
- unsigned shadercallcoherent : 1;
- unsigned nonprivate : 1;
- unsigned volatil : 1;
- unsigned isImage : 1;
- unsigned nonUniform : 1;
- void clear() {
- coherent = 0;
- devicecoherent = 0;
- queuefamilycoherent = 0;
- workgroupcoherent = 0;
- subgroupcoherent = 0;
- shadercallcoherent = 0;
- nonprivate = 0;
- volatil = 0;
- isImage = 0;
- nonUniform = 0;
- }
- CoherentFlags operator |=(const CoherentFlags &other) {
- coherent |= other.coherent;
- devicecoherent |= other.devicecoherent;
- queuefamilycoherent |= other.queuefamilycoherent;
- workgroupcoherent |= other.workgroupcoherent;
- subgroupcoherent |= other.subgroupcoherent;
- shadercallcoherent |= other.shadercallcoherent;
- nonprivate |= other.nonprivate;
- volatil |= other.volatil;
- isImage |= other.isImage;
- nonUniform |= other.nonUniform;
- return *this;
- }
- #endif
- };
- CoherentFlags coherentFlags;
- };
- //
- // the SPIR-V builder maintains a single active chain that
- // the following methods operate on
- //
- // for external save and restore
- AccessChain getAccessChain() { return accessChain; }
- void setAccessChain(AccessChain newChain) { accessChain = newChain; }
- // clear accessChain
- void clearAccessChain();
- // set new base as an l-value base
- void setAccessChainLValue(Id lValue)
- {
- assert(isPointer(lValue));
- accessChain.base = lValue;
- }
- // set new base value as an r-value
- void setAccessChainRValue(Id rValue)
- {
- accessChain.isRValue = true;
- accessChain.base = rValue;
- }
- // push offset onto the end of the chain
- void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
- {
- accessChain.indexChain.push_back(offset);
- accessChain.coherentFlags |= coherentFlags;
- accessChain.alignment |= alignment;
- }
- // push new swizzle onto the end of any existing swizzle, merging into a single swizzle
- void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,
- AccessChain::CoherentFlags coherentFlags, unsigned int alignment);
- // push a dynamic component selection onto the access chain, only applicable with a
- // non-trivial swizzle or no swizzle
- void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags,
- unsigned int alignment)
- {
- if (accessChain.swizzle.size() != 1) {
- accessChain.component = component;
- if (accessChain.preSwizzleBaseType == NoType)
- accessChain.preSwizzleBaseType = preSwizzleBaseType;
- }
- accessChain.coherentFlags |= coherentFlags;
- accessChain.alignment |= alignment;
- }
- // use accessChain and swizzle to store value
- void accessChainStore(Id rvalue, Decoration nonUniform,
- spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
- spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
- // use accessChain and swizzle to load an r-value
- Id accessChainLoad(Decoration precision, Decoration l_nonUniform, Decoration r_nonUniform, Id ResultType,
- spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax,
- unsigned int alignment = 0);
- // Return whether or not the access chain can be represented in SPIR-V
- // as an l-value.
- // E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be.
- bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; }
- // get the direct pointer for an l-value
- Id accessChainGetLValue();
- // Get the inferred SPIR-V type of the result of the current access chain,
- // based on the type of the base and the chain of dereferences.
- Id accessChainGetInferredType();
- // Add capabilities, extensions, remove unneeded decorations, etc.,
- // based on the resulting SPIR-V.
- void postProcess();
- // Prune unreachable blocks in the CFG and remove unneeded decorations.
- void postProcessCFG();
- #ifndef GLSLANG_WEB
- // Add capabilities, extensions based on instructions in the module.
- void postProcessFeatures();
- // Hook to visit each instruction in a block in a function
- void postProcess(Instruction&);
- // Hook to visit each non-32-bit sized float/int operation in a block.
- void postProcessType(const Instruction&, spv::Id typeId);
- #endif
- void dump(std::vector<unsigned int>&) const;
- void createBranch(Block* block);
- void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
- void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
- const std::vector<unsigned int>& operands);
- // Sets to generate opcode for specialization constants.
- void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }
- // Sets to generate opcode for non-specialization constants (normal mode).
- void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; }
- // Check if the builder is generating code for spec constants.
- bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; }
- protected:
- Id makeIntConstant(Id typeId, unsigned value, bool specConstant);
- Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant);
- Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value);
- Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2);
- Id findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps);
- Id findStructConstant(Id typeId, const std::vector<Id>& comps);
- Id collapseAccessChain();
- void remapDynamicSwizzle();
- void transferAccessChainSwizzle(bool dynamic);
- void simplifyAccessChainSwizzle();
- void createAndSetNoPredecessorBlock(const char*);
- void createSelectionMerge(Block* mergeBlock, unsigned int control);
- void dumpSourceInstructions(std::vector<unsigned int>&) const;
- void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const;
- void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const;
- void dumpModuleProcesses(std::vector<unsigned int>&) const;
- spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)
- const;
- unsigned int spvVersion; // the version of SPIR-V to emit in the header
- SourceLanguage source;
- int sourceVersion;
- spv::Id sourceFileStringId;
- std::string sourceText;
- int currentLine;
- const char* currentFile;
- bool emitOpLines;
- std::set<std::string> extensions;
- std::vector<const char*> sourceExtensions;
- std::vector<const char*> moduleProcesses;
- AddressingModel addressModel;
- MemoryModel memoryModel;
- std::set<spv::Capability> capabilities;
- int builderNumber;
- Module module;
- Block* buildPoint;
- Id uniqueId;
- Function* entryPointFunction;
- bool generatingOpCodeForSpecConst;
- AccessChain accessChain;
- // special blocks of instructions for output
- std::vector<std::unique_ptr<Instruction> > strings;
- std::vector<std::unique_ptr<Instruction> > imports;
- std::vector<std::unique_ptr<Instruction> > entryPoints;
- std::vector<std::unique_ptr<Instruction> > executionModes;
- std::vector<std::unique_ptr<Instruction> > names;
- std::vector<std::unique_ptr<Instruction> > decorations;
- std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals;
- std::vector<std::unique_ptr<Instruction> > externals;
- std::vector<std::unique_ptr<Function> > functions;
- // not output, internally used for quick & dirty canonical (unique) creation
- // map type opcodes to constant inst.
- std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants;
- // map struct-id to constant instructions
- std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants;
- // map type opcodes to type instructions
- std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes;
- // list of OpConstantNull instructions
- std::vector<Instruction*> nullConstants;
- // stack of switches
- std::stack<Block*> switchMerges;
- // Our loop stack.
- std::stack<LoopBlocks> loops;
- // map from strings to their string ids
- std::unordered_map<std::string, spv::Id> stringIds;
- // map from include file name ids to their contents
- std::map<spv::Id, const std::string*> includeFiles;
- // The stream for outputting warnings and errors.
- SpvBuildLogger* logger;
- }; // end Builder class
- }; // end spv namespace
- #endif // SpvBuilder_H
|