Path: blob/master/thirdparty/glslang/SPIRV/SpvBuilder.h
9902 views
//1// Copyright (C) 2014-2015 LunarG, Inc.2// Copyright (C) 2015-2020 Google, Inc.3// Copyright (C) 2017 ARM Limited.4// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.5//6// All rights reserved.7//8// Redistribution and use in source and binary forms, with or without9// modification, are permitted provided that the following conditions10// are met:11//12// Redistributions of source code must retain the above copyright13// notice, this list of conditions and the following disclaimer.14//15// Redistributions in binary form must reproduce the above16// copyright notice, this list of conditions and the following17// disclaimer in the documentation and/or other materials provided18// with the distribution.19//20// Neither the name of 3Dlabs Inc. Ltd. nor the names of its21// contributors may be used to endorse or promote products derived22// from this software without specific prior written permission.23//24// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS25// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT26// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS27// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE28// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,29// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,30// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;31// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER32// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT33// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN34// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE35// POSSIBILITY OF SUCH DAMAGE.3637//38// "Builder" is an interface to fully build SPIR-V IR. Allocate one of39// these to build (a thread safe) internal SPIR-V representation (IR),40// and then dump it as a binary stream according to the SPIR-V specification.41//42// A Builder has a 1:1 relationship with a SPIR-V module.43//4445#pragma once46#ifndef SpvBuilder_H47#define SpvBuilder_H4849#include "Logger.h"50#include "spirv.hpp"51#include "spvIR.h"52namespace spv {53#include "GLSL.ext.KHR.h"54#include "NonSemanticShaderDebugInfo100.h"55}5657#include <algorithm>58#include <cstdint>59#include <map>60#include <memory>61#include <set>62#include <sstream>63#include <stack>64#include <unordered_map>65#include <map>6667namespace spv {6869typedef enum {70Spv_1_0 = (1 << 16),71Spv_1_1 = (1 << 16) | (1 << 8),72Spv_1_2 = (1 << 16) | (2 << 8),73Spv_1_3 = (1 << 16) | (3 << 8),74Spv_1_4 = (1 << 16) | (4 << 8),75Spv_1_5 = (1 << 16) | (5 << 8),76} SpvVersion;7778class Builder {79public:80Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger);81virtual ~Builder();8283static const int maxMatrixSize = 4;8485unsigned int getSpvVersion() const { return spvVersion; }8687void setSource(spv::SourceLanguage lang, int version)88{89sourceLang = lang;90sourceVersion = version;91}92spv::Id getStringId(const std::string& str)93{94auto sItr = stringIds.find(str);95if (sItr != stringIds.end())96return sItr->second;97spv::Id strId = getUniqueId();98Instruction* fileString = new Instruction(strId, NoType, OpString);99const char* file_c_str = str.c_str();100fileString->addStringOperand(file_c_str);101strings.push_back(std::unique_ptr<Instruction>(fileString));102module.mapInstruction(fileString);103stringIds[file_c_str] = strId;104return strId;105}106107spv::Id getMainFileId() const { return mainFileId; }108109// Initialize the main source file name110void setDebugSourceFile(const std::string& file)111{112if (trackDebugInfo) {113dirtyLineTracker = true;114mainFileId = getStringId(file);115currentFileId = mainFileId;116}117}118119// Set the debug source location tracker in the builder.120// The upcoming instructions in basic blocks will be associated to this location.121void setDebugSourceLocation(int line, const char* filename)122{123if (trackDebugInfo) {124dirtyLineTracker = true;125if (line != 0) {126// TODO: This is special handling of some AST nodes having (untracked) line 0.127// But they should have a valid line number.128currentLine = line;129if (filename) {130currentFileId = getStringId(filename);131}132}133}134}135136void setSourceText(const std::string& text) { sourceText = text; }137void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); }138void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); }139void setEmitSpirvDebugInfo()140{141trackDebugInfo = true;142emitSpirvDebugInfo = true;143}144void setEmitNonSemanticShaderDebugInfo(bool emitSourceText)145{146trackDebugInfo = true;147emitNonSemanticShaderDebugInfo = true;148importNonSemanticShaderDebugInfoInstructions();149150if (emitSourceText) {151emitNonSemanticShaderDebugSource = emitSourceText;152}153}154void addExtension(const char* ext) { extensions.insert(ext); }155void removeExtension(const char* ext)156{157extensions.erase(ext);158}159void addIncorporatedExtension(const char* ext, SpvVersion incorporatedVersion)160{161if (getSpvVersion() < static_cast<unsigned>(incorporatedVersion))162addExtension(ext);163}164void promoteIncorporatedExtension(const char* baseExt, const char* promoExt, SpvVersion incorporatedVersion)165{166removeExtension(baseExt);167addIncorporatedExtension(promoExt, incorporatedVersion);168}169void addInclude(const std::string& name, const std::string& text)170{171spv::Id incId = getStringId(name);172includeFiles[incId] = &text;173}174Id import(const char*);175void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem)176{177addressModel = addr;178memoryModel = mem;179}180181void addCapability(spv::Capability cap) { capabilities.insert(cap); }182183// To get a new <id> for anything needing a new one.184Id getUniqueId() { return ++uniqueId; }185186// To get a set of new <id>s, e.g., for a set of function parameters187Id getUniqueIds(int numIds)188{189Id id = uniqueId + 1;190uniqueId += numIds;191return id;192}193194// For creating new types (will return old type if the requested one was already made).195Id makeVoidType();196Id makeBoolType();197Id makePointer(StorageClass, Id pointee);198Id makeForwardPointer(StorageClass);199Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee);200Id makeIntegerType(int width, bool hasSign); // generic201Id makeIntType(int width) { return makeIntegerType(width, true); }202Id makeUintType(int width) { return makeIntegerType(width, false); }203Id makeFloatType(int width);204Id makeStructType(const std::vector<Id>& members, const char* name, bool const compilerGenerated = true);205Id makeStructResultType(Id type0, Id type1);206Id makeVectorType(Id component, int size);207Id makeMatrixType(Id component, int cols, int rows);208Id makeArrayType(Id element, Id sizeId, int stride); // 0 stride means no stride decoration209Id makeRuntimeArray(Id element);210Id makeFunctionType(Id returnType, const std::vector<Id>& paramTypes);211Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format);212Id makeSamplerType();213Id makeSampledImageType(Id imageType);214Id makeCooperativeMatrixTypeKHR(Id component, Id scope, Id rows, Id cols, Id use);215Id makeCooperativeMatrixTypeNV(Id component, Id scope, Id rows, Id cols);216Id makeCooperativeMatrixTypeWithSameShape(Id component, Id otherType);217Id makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands);218219// SPIR-V NonSemantic Shader DebugInfo Instructions220struct DebugTypeLoc {221std::string name {};222int line {0};223int column {0};224};225std::unordered_map<Id, DebugTypeLoc> debugTypeLocs;226Id makeDebugInfoNone();227Id makeBoolDebugType(int const size);228Id makeIntegerDebugType(int const width, bool const hasSign);229Id makeFloatDebugType(int const width);230Id makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfo100Instructions const sequenceType);231Id makeArrayDebugType(Id const baseType, Id const componentCount);232Id makeVectorDebugType(Id const baseType, int const componentCount);233Id makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor = true);234Id makeMemberDebugType(Id const memberType, DebugTypeLoc const& debugTypeLoc);235Id makeCompositeDebugType(std::vector<Id> const& memberTypes, char const*const name,236NonSemanticShaderDebugInfo100DebugCompositeType const tag, bool const isOpaqueType = false);237Id makePointerDebugType(StorageClass storageClass, Id const baseType);238Id makeDebugSource(const Id fileName);239Id makeDebugCompilationUnit();240Id createDebugGlobalVariable(Id const type, char const*const name, Id const variable);241Id createDebugLocalVariable(Id type, char const*const name, size_t const argNumber = 0);242Id makeDebugExpression();243Id makeDebugDeclare(Id const debugLocalVariable, Id const pointer);244Id makeDebugValue(Id const debugLocalVariable, Id const value);245Id makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes);246Id makeDebugFunction(Function* function, Id nameId, Id funcTypeId);247Id makeDebugLexicalBlock(uint32_t line);248std::string unmangleFunctionName(std::string const& name) const;249void setupDebugFunctionEntry(Function* function, const char* name, int line,250const std::vector<Id>& paramTypes,251const std::vector<char const*>& paramNames);252253// accelerationStructureNV type254Id makeAccelerationStructureType();255// rayQueryEXT type256Id makeRayQueryType();257// hitObjectNV type258Id makeHitObjectNVType();259260// For querying about types.261Id getTypeId(Id resultId) const { return module.getTypeId(resultId); }262Id getDerefTypeId(Id resultId) const;263Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); }264Op getTypeClass(Id typeId) const { return getOpCode(typeId); }265Op getMostBasicTypeClass(Id typeId) const;266int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); }267int getNumTypeConstituents(Id typeId) const;268int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); }269Id getScalarTypeId(Id typeId) const;270Id getContainedTypeId(Id typeId) const;271Id getContainedTypeId(Id typeId, int) const;272StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); }273ImageFormat getImageTypeFormat(Id typeId) const274{ return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); }275Id getResultingAccessChainType() const;276Id getIdOperand(Id resultId, int idx) { return module.getInstruction(resultId)->getIdOperand(idx); }277278bool isPointer(Id resultId) const { return isPointerType(getTypeId(resultId)); }279bool isScalar(Id resultId) const { return isScalarType(getTypeId(resultId)); }280bool isVector(Id resultId) const { return isVectorType(getTypeId(resultId)); }281bool isMatrix(Id resultId) const { return isMatrixType(getTypeId(resultId)); }282bool isCooperativeMatrix(Id resultId)const { return isCooperativeMatrixType(getTypeId(resultId)); }283bool isAggregate(Id resultId) const { return isAggregateType(getTypeId(resultId)); }284bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); }285286bool isBoolType(Id typeId)287{ return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); }288bool isIntType(Id typeId) const289{ return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; }290bool isUintType(Id typeId) const291{ return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; }292bool isFloatType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat; }293bool isPointerType(Id typeId) const { return getTypeClass(typeId) == OpTypePointer; }294bool isScalarType(Id typeId) const295{ return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt ||296getTypeClass(typeId) == OpTypeBool; }297bool isVectorType(Id typeId) const { return getTypeClass(typeId) == OpTypeVector; }298bool isMatrixType(Id typeId) const { return getTypeClass(typeId) == OpTypeMatrix; }299bool isStructType(Id typeId) const { return getTypeClass(typeId) == OpTypeStruct; }300bool isArrayType(Id typeId) const { return getTypeClass(typeId) == OpTypeArray; }301bool isCooperativeMatrixType(Id typeId)const302{303return getTypeClass(typeId) == OpTypeCooperativeMatrixKHR || getTypeClass(typeId) == OpTypeCooperativeMatrixNV;304}305bool isAggregateType(Id typeId) const306{ return isArrayType(typeId) || isStructType(typeId) || isCooperativeMatrixType(typeId); }307bool isImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeImage; }308bool isSamplerType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampler; }309bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; }310bool containsType(Id typeId, Op typeOp, unsigned int width) const;311bool containsPhysicalStorageBufferOrArray(Id typeId) const;312313bool isConstantOpCode(Op opcode) const;314bool isSpecConstantOpCode(Op opcode) const;315bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); }316bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }317bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); }318unsigned int getConstantScalar(Id resultId) const319{ return module.getInstruction(resultId)->getImmediateOperand(0); }320StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); }321322bool isVariableOpCode(Op opcode) const { return opcode == OpVariable; }323bool isVariable(Id resultId) const { return isVariableOpCode(getOpCode(resultId)); }324bool isGlobalStorage(Id resultId) const { return getStorageClass(resultId) != StorageClassFunction; }325bool isGlobalVariable(Id resultId) const { return isVariable(resultId) && isGlobalStorage(resultId); }326// See if a resultId is valid for use as an initializer.327bool isValidInitializer(Id resultId) const { return isConstant(resultId) || isGlobalVariable(resultId); }328329int getScalarTypeWidth(Id typeId) const330{331Id scalarTypeId = getScalarTypeId(typeId);332assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat);333return module.getInstruction(scalarTypeId)->getImmediateOperand(0);334}335336int getTypeNumColumns(Id typeId) const337{338assert(isMatrixType(typeId));339return getNumTypeConstituents(typeId);340}341int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); }342int getTypeNumRows(Id typeId) const343{344assert(isMatrixType(typeId));345return getNumTypeComponents(getContainedTypeId(typeId));346}347int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); }348349Dim getTypeDimensionality(Id typeId) const350{351assert(isImageType(typeId));352return (Dim)module.getInstruction(typeId)->getImmediateOperand(1);353}354Id getImageType(Id resultId) const355{356Id typeId = getTypeId(resultId);357assert(isImageType(typeId) || isSampledImageType(typeId));358return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId;359}360bool isArrayedImageType(Id typeId) const361{362assert(isImageType(typeId));363return module.getInstruction(typeId)->getImmediateOperand(3) != 0;364}365366// For making new constants (will return old constant if the requested one was already made).367Id makeNullConstant(Id typeId);368Id makeBoolConstant(bool b, bool specConstant = false);369Id makeInt8Constant(int i, bool specConstant = false)370{ return makeIntConstant(makeIntType(8), (unsigned)i, specConstant); }371Id makeUint8Constant(unsigned u, bool specConstant = false)372{ return makeIntConstant(makeUintType(8), u, specConstant); }373Id makeInt16Constant(int i, bool specConstant = false)374{ return makeIntConstant(makeIntType(16), (unsigned)i, specConstant); }375Id makeUint16Constant(unsigned u, bool specConstant = false)376{ return makeIntConstant(makeUintType(16), u, specConstant); }377Id makeIntConstant(int i, bool specConstant = false)378{ return makeIntConstant(makeIntType(32), (unsigned)i, specConstant); }379Id makeUintConstant(unsigned u, bool specConstant = false)380{ return makeIntConstant(makeUintType(32), u, specConstant); }381Id makeInt64Constant(long long i, bool specConstant = false)382{ return makeInt64Constant(makeIntType(64), (unsigned long long)i, specConstant); }383Id makeUint64Constant(unsigned long long u, bool specConstant = false)384{ return makeInt64Constant(makeUintType(64), u, specConstant); }385Id makeFloatConstant(float f, bool specConstant = false);386Id makeDoubleConstant(double d, bool specConstant = false);387Id makeFloat16Constant(float f16, bool specConstant = false);388Id makeFpConstant(Id type, double d, bool specConstant = false);389390Id importNonSemanticShaderDebugInfoInstructions();391392// Turn the array of constants into a proper spv constant of the requested type.393Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false);394395// Methods for adding information outside the CFG.396Instruction* addEntryPoint(ExecutionModel, Function*, const char* name);397void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1);398void addExecutionMode(Function*, ExecutionMode mode, const std::vector<unsigned>& literals);399void addExecutionModeId(Function*, ExecutionMode mode, const std::vector<Id>& operandIds);400void addName(Id, const char* name);401void addMemberName(Id, int member, const char* name);402void addDecoration(Id, Decoration, int num = -1);403void addDecoration(Id, Decoration, const char*);404void addDecoration(Id, Decoration, const std::vector<unsigned>& literals);405void addDecoration(Id, Decoration, const std::vector<const char*>& strings);406void addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType);407void addDecorationId(Id id, Decoration, Id idDecoration);408void addDecorationId(Id id, Decoration, const std::vector<Id>& operandIds);409void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);410void addMemberDecoration(Id, unsigned int member, Decoration, const char*);411void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<unsigned>& literals);412void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<const char*>& strings);413414// At the end of what block do the next create*() instructions go?415// Also reset current last DebugScope and current source line to unknown416void setBuildPoint(Block* bp) {417buildPoint = bp;418// TODO: Technically, change of build point should set line tracker dirty. But we'll have bad line info for419// branch instructions. Commenting this for now because at least this matches the old behavior.420dirtyScopeTracker = true;421}422Block* getBuildPoint() const { return buildPoint; }423424// Append an instruction to the end of the current build point.425// Optionally, additional debug info instructions may also be prepended.426void addInstruction(std::unique_ptr<Instruction> inst);427428// Make the entry-point function. The returned pointer is only valid429// for the lifetime of this builder.430Function* makeEntryPoint(const char*);431432// Make a shader-style function, and create its entry block if entry is non-zero.433// Return the function, pass back the entry.434// The returned pointer is only valid for the lifetime of this builder.435Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType,436const std::vector<Id>& paramTypes,437const std::vector<std::vector<Decoration>>& precisions, Block** entry = nullptr);438439// Create a return. An 'implicit' return is one not appearing in the source440// code. In the case of an implicit return, no post-return block is inserted.441void makeReturn(bool implicit, Id retVal = 0);442443// Initialize state and generate instructions for new lexical scope444void enterLexicalBlock(uint32_t line);445446// Set state and generate instructions to exit current lexical scope447void leaveLexicalBlock();448449// Prepare builder for generation of instructions for a function.450void enterFunction(Function const* function);451452// Generate all the code needed to finish up a function.453void leaveFunction();454455// Create block terminator instruction for certain statements like456// discard, terminate-invocation, terminateRayEXT, or ignoreIntersectionEXT457void makeStatementTerminator(spv::Op opcode, const char *name);458459// Create block terminator instruction for statements that have input operands460// such as OpEmitMeshTasksEXT461void makeStatementTerminator(spv::Op opcode, const std::vector<Id>& operands, const char* name);462463// Create a global or function local or IO variable.464Id createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name = nullptr,465Id initializer = NoResult, bool const compilerGenerated = true);466467// Create an intermediate with an undefined value.468Id createUndefined(Id type);469470// Store into an Id and return the l-value471void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,472spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);473474// Load from an Id and return it475Id createLoad(Id lValue, spv::Decoration precision,476spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,477spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);478479// Create an OpAccessChain instruction480Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets);481482// Create an OpArrayLength instruction483Id createArrayLength(Id base, unsigned int member);484485// Create an OpCooperativeMatrixLengthKHR instruction486Id createCooperativeMatrixLengthKHR(Id type);487// Create an OpCooperativeMatrixLengthNV instruction488Id createCooperativeMatrixLengthNV(Id type);489490// Create an OpCompositeExtract instruction491Id createCompositeExtract(Id composite, Id typeId, unsigned index);492Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes);493Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);494Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes);495496Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex);497Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex);498499void createNoResultOp(Op);500void createNoResultOp(Op, Id operand);501void createNoResultOp(Op, const std::vector<Id>& operands);502void createNoResultOp(Op, const std::vector<IdImmediate>& operands);503void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask);504void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);505Id createUnaryOp(Op, Id typeId, Id operand);506Id createBinOp(Op, Id typeId, Id operand1, Id operand2);507Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);508Id createOp(Op, Id typeId, const std::vector<Id>& operands);509Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands);510Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&);511Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals);512513// Take an rvalue (source) and a set of channels to extract from it to514// make a new rvalue, which is returned.515Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels);516517// Take a copy of an lvalue (target) and a source of components, and set the518// source components into the lvalue where the 'channels' say to put them.519// An updated version of the target is returned.520// (No true lvalue or stores are used.)521Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels);522523// If both the id and precision are valid, the id524// gets tagged with the requested precision.525// The passed in id is always the returned id, to simplify use patterns.526Id setPrecision(Id id, Decoration precision)527{528if (precision != NoPrecision && id != NoResult)529addDecoration(id, precision);530531return id;532}533534// Can smear a scalar to a vector for the following forms:535// - promoteScalar(scalar, vector) // smear scalar to width of vector536// - promoteScalar(vector, scalar) // smear scalar to width of vector537// - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to538// - promoteScalar(scalar, scalar) // do nothing539// Other forms are not allowed.540//541// Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'.542// The type of the created vector is a vector of components of the same type as the scalar.543//544// Note: One of the arguments will change, with the result coming back that way rather than545// through the return value.546void promoteScalar(Decoration precision, Id& left, Id& right);547548// Make a value by smearing the scalar to fill the type.549// vectorType should be the correct type for making a vector of scalarVal.550// (No conversions are done.)551Id smearScalar(Decoration precision, Id scalarVal, Id vectorType);552553// Create a call to a built-in function.554Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args);555556// List of parameters used to create a texture operation557struct TextureParameters {558Id sampler;559Id coords;560Id bias;561Id lod;562Id Dref;563Id offset;564Id offsets;565Id gradX;566Id gradY;567Id sample;568Id component;569Id texelOut;570Id lodClamp;571Id granularity;572Id coarse;573bool nonprivate;574bool volatil;575};576577// Select the correct texture operation based on all inputs, and emit the correct instruction578Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,579bool noImplicit, const TextureParameters&, ImageOperandsMask);580581// Emit the OpTextureQuery* instruction that was passed in.582// Figure out the right return value and type, and return it.583Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult);584585Id createSamplePositionCall(Decoration precision, Id, Id);586587Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned);588Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id);589590// Reduction comparison for composites: For equal and not-equal resulting in a scalar.591Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */);592593// OpCompositeConstruct594Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents);595596// vector or scalar constructor597Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId);598599// matrix constructor600Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee);601602// Helper to use for building nested control flow with if-then-else.603class If {604public:605If(Id condition, unsigned int ctrl, Builder& builder);606~If() {}607608void makeBeginElse();609void makeEndIf();610611private:612If(const If&);613If& operator=(If&);614615Builder& builder;616Id condition;617unsigned int control;618Function* function;619Block* headerBlock;620Block* thenBlock;621Block* elseBlock;622Block* mergeBlock;623};624625// Make a switch statement. A switch has 'numSegments' of pieces of code, not containing626// any case/default labels, all separated by one or more case/default labels. Each possible627// case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this628// number space. How to compute the value is given by 'condition', as in switch(condition).629//630// The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.631//632// Use a defaultSegment < 0 if there is no default segment (to branch to post switch).633//634// Returns the right set of basic blocks to start each code segment with, so that the caller's635// recursion stack can hold the memory for it.636//637void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector<int>& caseValues,638const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB);639640// Add a branch to the innermost switch's merge block.641void addSwitchBreak();642643// Move to the next code segment, passing in the return argument in makeSwitch()644void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment);645646// Finish off the innermost switch.647void endSwitch(std::vector<Block*>& segmentBB);648649struct LoopBlocks {650LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) :651head(head), body(body), merge(merge), continue_target(continue_target) { }652Block &head, &body, &merge, &continue_target;653private:654LoopBlocks();655LoopBlocks& operator=(const LoopBlocks&) = delete;656};657658// Start a new loop and prepare the builder to generate code for it. Until659// closeLoop() is called for this loop, createLoopContinue() and660// createLoopExit() will target its corresponding blocks.661LoopBlocks& makeNewLoop();662663// Create a new block in the function containing the build point. Memory is664// owned by the function object.665Block& makeNewBlock();666667// Add a branch to the continue_target of the current (innermost) loop.668void createLoopContinue();669670// Add an exit (e.g. "break") from the innermost loop that we're currently671// in.672void createLoopExit();673674// Close the innermost loop that you're in675void closeLoop();676677//678// Access chain design for an R-Value vs. L-Value:679//680// There is a single access chain the builder is building at681// any particular time. Such a chain can be used to either to a load or682// a store, when desired.683//684// Expressions can be r-values, l-values, or both, or only r-values:685// a[b.c].d = .... // l-value686// ... = a[b.c].d; // r-value, that also looks like an l-value687// ++a[b.c].d; // r-value and l-value688// (x + y)[2]; // r-value only, can't possibly be l-value689//690// Computing an r-value means generating code. Hence,691// r-values should only be computed when they are needed, not speculatively.692//693// Computing an l-value means saving away information for later use in the compiler,694// no code is generated until the l-value is later dereferenced. It is okay695// to speculatively generate an l-value, just not okay to speculatively dereference it.696//697// The base of the access chain (the left-most variable or expression698// from which everything is based) can be set either as an l-value699// or as an r-value. Most efficient would be to set an l-value if one700// is available. If an expression was evaluated, the resulting r-value701// can be set as the chain base.702//703// The users of this single access chain can save and restore if they704// want to nest or manage multiple chains.705//706707struct AccessChain {708Id base; // for l-values, pointer to the base object, for r-values, the base object709std::vector<Id> indexChain;710Id instr; // cache the instruction that generates this access chain711std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number712Id component; // a dynamic component index, can coexist with a swizzle,713// done after the swizzle, NoResult if not present714Id preSwizzleBaseType; // dereferenced type, before swizzle or component is applied;715// NoType unless a swizzle or component is present716bool isRValue; // true if 'base' is an r-value, otherwise, base is an l-value717unsigned int alignment; // bitwise OR of alignment values passed in. Accumulates worst alignment.718// Only tracks base and (optional) component selection alignment.719720// Accumulate whether anything in the chain of structures has coherent decorations.721struct CoherentFlags {722CoherentFlags() { clear(); }723bool isVolatile() const { return volatil; }724bool isNonUniform() const { return nonUniform; }725bool anyCoherent() const {726return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent ||727subgroupcoherent || shadercallcoherent;728}729730unsigned coherent : 1;731unsigned devicecoherent : 1;732unsigned queuefamilycoherent : 1;733unsigned workgroupcoherent : 1;734unsigned subgroupcoherent : 1;735unsigned shadercallcoherent : 1;736unsigned nonprivate : 1;737unsigned volatil : 1;738unsigned isImage : 1;739unsigned nonUniform : 1;740741void clear() {742coherent = 0;743devicecoherent = 0;744queuefamilycoherent = 0;745workgroupcoherent = 0;746subgroupcoherent = 0;747shadercallcoherent = 0;748nonprivate = 0;749volatil = 0;750isImage = 0;751nonUniform = 0;752}753754CoherentFlags operator |=(const CoherentFlags &other) {755coherent |= other.coherent;756devicecoherent |= other.devicecoherent;757queuefamilycoherent |= other.queuefamilycoherent;758workgroupcoherent |= other.workgroupcoherent;759subgroupcoherent |= other.subgroupcoherent;760shadercallcoherent |= other.shadercallcoherent;761nonprivate |= other.nonprivate;762volatil |= other.volatil;763isImage |= other.isImage;764nonUniform |= other.nonUniform;765return *this;766}767};768CoherentFlags coherentFlags;769};770771//772// the SPIR-V builder maintains a single active chain that773// the following methods operate on774//775776// for external save and restore777AccessChain getAccessChain() { return accessChain; }778void setAccessChain(AccessChain newChain) { accessChain = newChain; }779780// clear accessChain781void clearAccessChain();782783// set new base as an l-value base784void setAccessChainLValue(Id lValue)785{786assert(isPointer(lValue));787accessChain.base = lValue;788}789790// set new base value as an r-value791void setAccessChainRValue(Id rValue)792{793accessChain.isRValue = true;794accessChain.base = rValue;795}796797// push offset onto the end of the chain798void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment)799{800accessChain.indexChain.push_back(offset);801accessChain.coherentFlags |= coherentFlags;802accessChain.alignment |= alignment;803}804805// push new swizzle onto the end of any existing swizzle, merging into a single swizzle806void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,807AccessChain::CoherentFlags coherentFlags, unsigned int alignment);808809// push a dynamic component selection onto the access chain, only applicable with a810// non-trivial swizzle or no swizzle811void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags,812unsigned int alignment)813{814if (accessChain.swizzle.size() != 1) {815accessChain.component = component;816if (accessChain.preSwizzleBaseType == NoType)817accessChain.preSwizzleBaseType = preSwizzleBaseType;818}819accessChain.coherentFlags |= coherentFlags;820accessChain.alignment |= alignment;821}822823// use accessChain and swizzle to store value824void accessChainStore(Id rvalue, Decoration nonUniform,825spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,826spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);827828// use accessChain and swizzle to load an r-value829Id accessChainLoad(Decoration precision, Decoration l_nonUniform, Decoration r_nonUniform, Id ResultType,830spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax,831unsigned int alignment = 0);832833// Return whether or not the access chain can be represented in SPIR-V834// as an l-value.835// E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be.836bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; }837838// get the direct pointer for an l-value839Id accessChainGetLValue();840841// Get the inferred SPIR-V type of the result of the current access chain,842// based on the type of the base and the chain of dereferences.843Id accessChainGetInferredType();844845// Add capabilities, extensions, remove unneeded decorations, etc.,846// based on the resulting SPIR-V.847void postProcess(bool compileOnly);848849// Prune unreachable blocks in the CFG and remove unneeded decorations.850void postProcessCFG();851852// Add capabilities, extensions based on instructions in the module.853void postProcessFeatures();854// Hook to visit each instruction in a block in a function855void postProcess(Instruction&);856// Hook to visit each non-32-bit sized float/int operation in a block.857void postProcessType(const Instruction&, spv::Id typeId);858859void dump(std::vector<unsigned int>&) const;860861void createBranch(Block* block);862void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);863void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,864const std::vector<unsigned int>& operands);865866// Sets to generate opcode for specialization constants.867void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }868// Sets to generate opcode for non-specialization constants (normal mode).869void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; }870// Check if the builder is generating code for spec constants.871bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; }872873protected:874Id makeIntConstant(Id typeId, unsigned value, bool specConstant);875Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant);876Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value);877Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2);878Id findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps);879Id findStructConstant(Id typeId, const std::vector<Id>& comps);880Id collapseAccessChain();881void remapDynamicSwizzle();882void transferAccessChainSwizzle(bool dynamic);883void simplifyAccessChainSwizzle();884void createAndSetNoPredecessorBlock(const char*);885void createSelectionMerge(Block* mergeBlock, unsigned int control);886void dumpSourceInstructions(std::vector<unsigned int>&) const;887void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const;888void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const;889void dumpModuleProcesses(std::vector<unsigned int>&) const;890spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)891const;892893unsigned int spvVersion; // the version of SPIR-V to emit in the header894SourceLanguage sourceLang;895int sourceVersion;896spv::Id nonSemanticShaderCompilationUnitId {0};897spv::Id nonSemanticShaderDebugInfo {0};898spv::Id debugInfoNone {0};899spv::Id debugExpression {0}; // Debug expression with zero operations.900std::string sourceText;901902// True if an new OpLine/OpDebugLine may need to be inserted. Either:903// 1. The current debug location changed904// 2. The current build point changed905bool dirtyLineTracker;906int currentLine = 0;907// OpString id of the current file name. Always 0 if debug info is off.908spv::Id currentFileId = 0;909// OpString id of the main file name. Always 0 if debug info is off.910spv::Id mainFileId = 0;911912// True if an new OpDebugScope may need to be inserted. Either:913// 1. A new lexical block is pushed914// 2. The current build point changed915bool dirtyScopeTracker;916std::stack<spv::Id> currentDebugScopeId;917918// This flag toggles tracking of debug info while building the SPIR-V.919bool trackDebugInfo = false;920// This flag toggles emission of SPIR-V debug instructions, like OpLine and OpSource.921bool emitSpirvDebugInfo = false;922// This flag toggles emission of Non-Semantic Debug extension debug instructions.923bool emitNonSemanticShaderDebugInfo = false;924bool restoreNonSemanticShaderDebugInfo = false;925bool emitNonSemanticShaderDebugSource = false;926927std::set<std::string> extensions;928std::vector<const char*> sourceExtensions;929std::vector<const char*> moduleProcesses;930AddressingModel addressModel;931MemoryModel memoryModel;932std::set<spv::Capability> capabilities;933int builderNumber;934Module module;935Block* buildPoint;936Id uniqueId;937Function* entryPointFunction;938bool generatingOpCodeForSpecConst;939AccessChain accessChain;940941// special blocks of instructions for output942std::vector<std::unique_ptr<Instruction> > strings;943std::vector<std::unique_ptr<Instruction> > imports;944std::vector<std::unique_ptr<Instruction> > entryPoints;945std::vector<std::unique_ptr<Instruction> > executionModes;946std::vector<std::unique_ptr<Instruction> > names;947std::vector<std::unique_ptr<Instruction> > decorations;948std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals;949std::vector<std::unique_ptr<Instruction> > externals;950std::vector<std::unique_ptr<Function> > functions;951952// not output, internally used for quick & dirty canonical (unique) creation953954// map type opcodes to constant inst.955std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants;956// map struct-id to constant instructions957std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants;958// map type opcodes to type instructions959std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes;960// map type opcodes to debug type instructions961std::unordered_map<unsigned int, std::vector<Instruction*>> groupedDebugTypes;962// list of OpConstantNull instructions963std::vector<Instruction*> nullConstants;964965// stack of switches966std::stack<Block*> switchMerges;967968// Our loop stack.969std::stack<LoopBlocks> loops;970971// map from strings to their string ids972std::unordered_map<std::string, spv::Id> stringIds;973974// map from include file name ids to their contents975std::map<spv::Id, const std::string*> includeFiles;976977// map from core id to debug id978std::map <spv::Id, spv::Id> debugId;979980// map from file name string id to DebugSource id981std::unordered_map<spv::Id, spv::Id> debugSourceId;982983// The stream for outputting warnings and errors.984SpvBuildLogger* logger;985}; // end Builder class986987}; // end spv namespace988989#endif // SpvBuilder_H990991992