Path: blob/master/thirdparty/glslang/SPIRV/SpvBuilder.cpp
21117 views
//1// Copyright (C) 2014-2015 LunarG, Inc.2// Copyright (C) 2015-2018 Google, Inc.3// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.4//5// All rights reserved.6//7// Redistribution and use in source and binary forms, with or without8// modification, are permitted provided that the following conditions9// are met:10//11// Redistributions of source code must retain the above copyright12// notice, this list of conditions and the following disclaimer.13//14// Redistributions in binary form must reproduce the above15// copyright notice, this list of conditions and the following16// disclaimer in the documentation and/or other materials provided17// with the distribution.18//19// Neither the name of 3Dlabs Inc. Ltd. nor the names of its20// contributors may be used to endorse or promote products derived21// from this software without specific prior written permission.22//23// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS24// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT25// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS26// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE27// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,28// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,29// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;30// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER31// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT32// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN33// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE34// POSSIBILITY OF SUCH DAMAGE.3536//37// Helper for making SPIR-V IR. Generally, this is documented in the header38// SpvBuilder.h.39//4041#include <cassert>42#include <cstdlib>4344#include <unordered_set>45#include <algorithm>4647#include "SpvBuilder.h"48#include "spvUtil.h"49#include "hex_float.h"5051#ifndef _WIN3252#include <cstdio>53#endif5455namespace spv {5657Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) :58spvVersion(spvVersion),59sourceLang(SourceLanguage::Unknown),60sourceVersion(0),61addressModel(AddressingModel::Logical),62memoryModel(MemoryModel::GLSL450),63builderNumber(magicNumber),64buildPoint(nullptr),65uniqueId(0),66entryPointFunction(nullptr),67generatingOpCodeForSpecConst(false),68logger(buildLogger)69{70clearAccessChain();71}7273Builder::~Builder()74{75}7677Id Builder::import(const char* name)78{79Instruction* import = new Instruction(getUniqueId(), NoType, Op::OpExtInstImport);80import->addStringOperand(name);81module.mapInstruction(import);8283imports.push_back(std::unique_ptr<Instruction>(import));84return import->getResultId();85}8687// For creating new groupedTypes (will return old type if the requested one was already made).88Id Builder::makeVoidType()89{90Instruction* type;91if (groupedTypes[enumCast(Op::OpTypeVoid)].size() == 0) {92Id typeId = getUniqueId();93type = new Instruction(typeId, NoType, Op::OpTypeVoid);94groupedTypes[enumCast(Op::OpTypeVoid)].push_back(type);95constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));96module.mapInstruction(type);97// Core OpTypeVoid used for debug void type98if (emitNonSemanticShaderDebugInfo)99debugTypeIdLookup[typeId] = typeId;100} else101type = groupedTypes[enumCast(Op::OpTypeVoid)].back();102103return type->getResultId();104}105106Id Builder::makeBoolType()107{108Instruction* type;109if (groupedTypes[enumCast(Op::OpTypeBool)].size() == 0) {110type = new Instruction(getUniqueId(), NoType, Op::OpTypeBool);111groupedTypes[enumCast(Op::OpTypeBool)].push_back(type);112constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));113module.mapInstruction(type);114115if (emitNonSemanticShaderDebugInfo) {116auto const debugResultId = makeBoolDebugType(32);117debugTypeIdLookup[type->getResultId()] = debugResultId;118}119120} else121type = groupedTypes[enumCast(Op::OpTypeBool)].back();122123124return type->getResultId();125}126127Id Builder::makeSamplerType(const char* debugName)128{129Instruction* type;130if (groupedTypes[enumCast(Op::OpTypeSampler)].size() == 0) {131type = new Instruction(getUniqueId(), NoType, Op::OpTypeSampler);132groupedTypes[enumCast(Op::OpTypeSampler)].push_back(type);133constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));134module.mapInstruction(type);135} else136type = groupedTypes[enumCast(Op::OpTypeSampler)].back();137138if (emitNonSemanticShaderDebugInfo)139{140auto const debugResultId = makeOpaqueDebugType(debugName);141debugTypeIdLookup[type->getResultId()] = debugResultId;142}143144return type->getResultId();145}146147Id Builder::makePointer(StorageClass storageClass, Id pointee)148{149// try to find it150Instruction* type;151for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypePointer)].size(); ++t) {152type = groupedTypes[enumCast(Op::OpTypePointer)][t];153if (type->getImmediateOperand(0) == (unsigned)storageClass &&154type->getIdOperand(1) == pointee)155return type->getResultId();156}157158// not found, make it159type = new Instruction(getUniqueId(), NoType, Op::OpTypePointer);160type->reserveOperands(2);161type->addImmediateOperand(storageClass);162type->addIdOperand(pointee);163groupedTypes[enumCast(Op::OpTypePointer)].push_back(type);164constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));165module.mapInstruction(type);166167if (emitNonSemanticShaderDebugInfo) {168const Id debugResultId = makePointerDebugType(storageClass, pointee);169debugTypeIdLookup[type->getResultId()] = debugResultId;170}171172return type->getResultId();173}174175Id Builder::makeForwardPointer(StorageClass storageClass)176{177// Caching/uniquifying doesn't work here, because we don't know the178// pointee type and there can be multiple forward pointers of the same179// storage type. Somebody higher up in the stack must keep track.180Instruction* type = new Instruction(getUniqueId(), NoType, Op::OpTypeForwardPointer);181type->addImmediateOperand(storageClass);182constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));183module.mapInstruction(type);184185if (emitNonSemanticShaderDebugInfo) {186const Id debugResultId = makeForwardPointerDebugType(storageClass);187debugTypeIdLookup[type->getResultId()] = debugResultId;188}189return type->getResultId();190}191192Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee)193{194// try to find it195Instruction* type;196for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypePointer)].size(); ++t) {197type = groupedTypes[enumCast(Op::OpTypePointer)][t];198if (type->getImmediateOperand(0) == (unsigned)storageClass &&199type->getIdOperand(1) == pointee)200return type->getResultId();201}202203type = new Instruction(forwardPointerType, NoType, Op::OpTypePointer);204type->reserveOperands(2);205type->addImmediateOperand(storageClass);206type->addIdOperand(pointee);207groupedTypes[enumCast(Op::OpTypePointer)].push_back(type);208constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));209module.mapInstruction(type);210211// If we are emitting nonsemantic debuginfo, we need to patch the debug pointer type212// that was emitted alongside the forward pointer, now that we have a pointee debug213// type for it to point to.214if (emitNonSemanticShaderDebugInfo) {215Instruction *debugForwardPointer = module.getInstruction(getDebugType(forwardPointerType));216assert(getDebugType(pointee));217debugForwardPointer->setIdOperand(2, getDebugType(pointee));218}219220return type->getResultId();221}222223Id Builder::makeIntegerType(int width, bool hasSign)224{225// try to find it226Instruction* type;227for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeInt)].size(); ++t) {228type = groupedTypes[enumCast(Op::OpTypeInt)][t];229if (type->getImmediateOperand(0) == (unsigned)width &&230type->getImmediateOperand(1) == (hasSign ? 1u : 0u))231return type->getResultId();232}233234// not found, make it235type = new Instruction(getUniqueId(), NoType, Op::OpTypeInt);236type->reserveOperands(2);237type->addImmediateOperand(width);238type->addImmediateOperand(hasSign ? 1 : 0);239groupedTypes[enumCast(Op::OpTypeInt)].push_back(type);240constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));241module.mapInstruction(type);242243// deal with capabilities244switch (width) {245case 8:246case 16:247// these are currently handled by storage-type declarations and post processing248break;249case 64:250addCapability(Capability::Int64);251break;252default:253break;254}255256if (emitNonSemanticShaderDebugInfo)257{258auto const debugResultId = makeIntegerDebugType(width, hasSign);259debugTypeIdLookup[type->getResultId()] = debugResultId;260}261262return type->getResultId();263}264265Id Builder::makeFloatType(int width)266{267// try to find it268Instruction* type;269for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeFloat)].size(); ++t) {270type = groupedTypes[enumCast(Op::OpTypeFloat)][t];271if (type->getNumOperands() != 1) {272continue;273}274if (type->getImmediateOperand(0) == (unsigned)width)275return type->getResultId();276}277278// not found, make it279type = new Instruction(getUniqueId(), NoType, Op::OpTypeFloat);280type->addImmediateOperand(width);281groupedTypes[enumCast(Op::OpTypeFloat)].push_back(type);282constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));283module.mapInstruction(type);284285// deal with capabilities286switch (width) {287case 16:288// currently handled by storage-type declarations and post processing289break;290case 64:291addCapability(Capability::Float64);292break;293default:294break;295}296297if (emitNonSemanticShaderDebugInfo)298{299auto const debugResultId = makeFloatDebugType(width);300debugTypeIdLookup[type->getResultId()] = debugResultId;301}302303return type->getResultId();304}305306Id Builder::makeBFloat16Type()307{308// try to find it309Instruction* type;310for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeFloat)].size(); ++t) {311type = groupedTypes[enumCast(Op::OpTypeFloat)][t];312if (type->getNumOperands() != 2) {313continue;314}315if (type->getImmediateOperand(0) == (unsigned)16 &&316type->getImmediateOperand(1) == FPEncoding::BFloat16KHR)317return type->getResultId();318}319320// not found, make it321type = new Instruction(getUniqueId(), NoType, Op::OpTypeFloat);322type->addImmediateOperand(16);323type->addImmediateOperand(FPEncoding::BFloat16KHR);324groupedTypes[enumCast(Op::OpTypeFloat)].push_back(type);325constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));326module.mapInstruction(type);327328addExtension(spv::E_SPV_KHR_bfloat16);329addCapability(Capability::BFloat16TypeKHR);330331#if 0332// XXX not supported333if (emitNonSemanticShaderDebugInfo)334{335auto const debugResultId = makeFloatDebugType(width);336debugTypeIdLookup[type->getResultId()] = debugResultId;337}338#endif339340return type->getResultId();341}342343Id Builder::makeFloatE5M2Type()344{345// try to find it346Instruction* type;347for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeFloat)].size(); ++t) {348type = groupedTypes[enumCast(Op::OpTypeFloat)][t];349if (type->getNumOperands() != 2) {350continue;351}352if (type->getImmediateOperand(0) == (unsigned)8 &&353type->getImmediateOperand(1) == FPEncoding::Float8E5M2EXT)354return type->getResultId();355}356357// not found, make it358type = new Instruction(getUniqueId(), NoType, Op::OpTypeFloat);359type->addImmediateOperand(8);360type->addImmediateOperand(FPEncoding::Float8E5M2EXT);361groupedTypes[enumCast(Op::OpTypeFloat)].push_back(type);362constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));363module.mapInstruction(type);364365addExtension(spv::E_SPV_EXT_float8);366addCapability(Capability::Float8EXT);367368#if 0369// XXX not supported370if (emitNonSemanticShaderDebugInfo)371{372auto const debugResultId = makeFloatDebugType(width);373debugTypeIdLookup[type->getResultId()] = debugResultId;374}375#endif376377return type->getResultId();378}379380Id Builder::makeFloatE4M3Type()381{382// try to find it383Instruction* type;384for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeFloat)].size(); ++t) {385type = groupedTypes[enumCast(Op::OpTypeFloat)][t];386if (type->getNumOperands() != 2) {387continue;388}389if (type->getImmediateOperand(0) == (unsigned)8 &&390type->getImmediateOperand(1) == FPEncoding::Float8E4M3EXT)391return type->getResultId();392}393394// not found, make it395type = new Instruction(getUniqueId(), NoType, Op::OpTypeFloat);396type->addImmediateOperand(8);397type->addImmediateOperand(FPEncoding::Float8E4M3EXT);398groupedTypes[enumCast(Op::OpTypeFloat)].push_back(type);399constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));400module.mapInstruction(type);401402addExtension(spv::E_SPV_EXT_float8);403addCapability(Capability::Float8EXT);404405#if 0406// XXX not supported407if (emitNonSemanticShaderDebugInfo)408{409auto const debugResultId = makeFloatDebugType(width);410debugTypeIdLookup[type->getResultId()] = debugResultId;411}412#endif413414return type->getResultId();415}416417// Make a struct without checking for duplication.418// See makeStructResultType() for non-decorated structs419// needed as the result of some instructions, which does420// check for duplicates.421// For compiler-generated structs, debug info is ignored.422Id Builder::makeStructType(const std::vector<Id>& members, const std::vector<spv::StructMemberDebugInfo>& memberDebugInfo,423const char* name, bool const compilerGenerated)424{425// Don't look for previous one, because in the general case,426// structs can be duplicated except for decorations.427428// not found, make it429Instruction* type = new Instruction(getUniqueId(), NoType, Op::OpTypeStruct);430for (int op = 0; op < (int)members.size(); ++op)431type->addIdOperand(members[op]);432groupedTypes[enumCast(Op::OpTypeStruct)].push_back(type);433constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));434module.mapInstruction(type);435addName(type->getResultId(), name);436437if (emitNonSemanticShaderDebugInfo && !compilerGenerated) {438assert(members.size() == memberDebugInfo.size());439auto const debugResultId =440makeCompositeDebugType(members, memberDebugInfo, name, NonSemanticShaderDebugInfo100Structure);441debugTypeIdLookup[type->getResultId()] = debugResultId;442}443444return type->getResultId();445}446447// Make a struct for the simple results of several instructions,448// checking for duplication.449Id Builder::makeStructResultType(Id type0, Id type1)450{451// try to find it452Instruction* type;453for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeStruct)].size(); ++t) {454type = groupedTypes[enumCast(Op::OpTypeStruct)][t];455if (type->getNumOperands() != 2)456continue;457if (type->getIdOperand(0) != type0 ||458type->getIdOperand(1) != type1)459continue;460return type->getResultId();461}462463// not found, make it464std::vector<spv::Id> members;465members.push_back(type0);466members.push_back(type1);467468return makeStructType(members, {}, "ResType");469}470471Id Builder::makeVectorType(Id component, int size)472{473// try to find it474Instruction* type;475for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeVector)].size(); ++t) {476type = groupedTypes[enumCast(Op::OpTypeVector)][t];477if (type->getIdOperand(0) == component &&478type->getImmediateOperand(1) == (unsigned)size)479return type->getResultId();480}481482// not found, make it483type = new Instruction(getUniqueId(), NoType, Op::OpTypeVector);484type->reserveOperands(2);485type->addIdOperand(component);486type->addImmediateOperand(size);487groupedTypes[enumCast(Op::OpTypeVector)].push_back(type);488constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));489module.mapInstruction(type);490491if (emitNonSemanticShaderDebugInfo)492{493auto const debugResultId = makeVectorDebugType(component, size);494debugTypeIdLookup[type->getResultId()] = debugResultId;495}496497return type->getResultId();498}499500Id Builder::makeMatrixType(Id component, int cols, int rows)501{502assert(cols <= maxMatrixSize && rows <= maxMatrixSize);503504Id column = makeVectorType(component, rows);505506// try to find it507Instruction* type;508for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeMatrix)].size(); ++t) {509type = groupedTypes[enumCast(Op::OpTypeMatrix)][t];510if (type->getIdOperand(0) == column &&511type->getImmediateOperand(1) == (unsigned)cols)512return type->getResultId();513}514515// not found, make it516type = new Instruction(getUniqueId(), NoType, Op::OpTypeMatrix);517type->reserveOperands(2);518type->addIdOperand(column);519type->addImmediateOperand(cols);520groupedTypes[enumCast(Op::OpTypeMatrix)].push_back(type);521constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));522module.mapInstruction(type);523524if (emitNonSemanticShaderDebugInfo)525{526auto const debugResultId = makeMatrixDebugType(column, cols);527debugTypeIdLookup[type->getResultId()] = debugResultId;528}529530return type->getResultId();531}532533Id Builder::makeCooperativeMatrixTypeKHR(Id component, Id scope, Id rows, Id cols, Id use)534{535// try to find it536Instruction* type;537for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeCooperativeMatrixKHR)].size(); ++t) {538type = groupedTypes[enumCast(Op::OpTypeCooperativeMatrixKHR)][t];539if (type->getIdOperand(0) == component &&540type->getIdOperand(1) == scope &&541type->getIdOperand(2) == rows &&542type->getIdOperand(3) == cols &&543type->getIdOperand(4) == use)544return type->getResultId();545}546547// not found, make it548type = new Instruction(getUniqueId(), NoType, Op::OpTypeCooperativeMatrixKHR);549type->reserveOperands(5);550type->addIdOperand(component);551type->addIdOperand(scope);552type->addIdOperand(rows);553type->addIdOperand(cols);554type->addIdOperand(use);555groupedTypes[enumCast(Op::OpTypeCooperativeMatrixKHR)].push_back(type);556constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));557module.mapInstruction(type);558559if (emitNonSemanticShaderDebugInfo)560{561// Find a name for one of the parameters. It can either come from debuginfo for another562// type, or an OpName from a constant.563auto const findName = [&](Id id) {564Id id2 = getDebugType(id);565for (auto &t : groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic]) {566if (t->getResultId() == id2) {567for (auto &s : strings) {568if (s->getResultId() == t->getIdOperand(2)) {569return s->getNameString();570}571}572}573}574for (auto &t : names) {575if (t->getIdOperand(0) == id) {576return t->getNameString();577}578}579return "unknown";580};581std::string debugName = "coopmat<";582debugName += std::string(findName(component)) + ", ";583if (isConstantScalar(scope)) {584debugName += std::string("gl_Scope") + std::string(spv::ScopeToString((spv::Scope)getConstantScalar(scope))) + ", ";585} else {586debugName += std::string(findName(scope)) + ", ";587}588debugName += std::string(findName(rows)) + ", ";589debugName += std::string(findName(cols)) + ">";590// There's no nonsemantic debug info instruction for cooperative matrix types,591// use opaque composite instead.592auto const debugResultId = makeOpaqueDebugType(debugName.c_str());593debugTypeIdLookup[type->getResultId()] = debugResultId;594}595596return type->getResultId();597}598599Id Builder::makeCooperativeMatrixTypeNV(Id component, Id scope, Id rows, Id cols)600{601// try to find it602Instruction* type;603for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeCooperativeMatrixNV)].size(); ++t) {604type = groupedTypes[enumCast(Op::OpTypeCooperativeMatrixNV)][t];605if (type->getIdOperand(0) == component && type->getIdOperand(1) == scope && type->getIdOperand(2) == rows &&606type->getIdOperand(3) == cols)607return type->getResultId();608}609610// not found, make it611type = new Instruction(getUniqueId(), NoType, Op::OpTypeCooperativeMatrixNV);612type->reserveOperands(4);613type->addIdOperand(component);614type->addIdOperand(scope);615type->addIdOperand(rows);616type->addIdOperand(cols);617groupedTypes[enumCast(Op::OpTypeCooperativeMatrixNV)].push_back(type);618constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));619module.mapInstruction(type);620621return type->getResultId();622}623624Id Builder::makeCooperativeMatrixTypeWithSameShape(Id component, Id otherType)625{626Instruction* instr = module.getInstruction(otherType);627if (instr->getOpCode() == Op::OpTypeCooperativeMatrixNV) {628return makeCooperativeMatrixTypeNV(component, instr->getIdOperand(1), instr->getIdOperand(2), instr->getIdOperand(3));629} else {630assert(instr->getOpCode() == Op::OpTypeCooperativeMatrixKHR);631return makeCooperativeMatrixTypeKHR(component, instr->getIdOperand(1), instr->getIdOperand(2), instr->getIdOperand(3), instr->getIdOperand(4));632}633}634635Id Builder::makeCooperativeVectorTypeNV(Id componentType, Id components)636{637// try to find it638Instruction* type;639for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeCooperativeVectorNV)].size(); ++t) {640type = groupedTypes[enumCast(Op::OpTypeCooperativeVectorNV)][t];641if (type->getIdOperand(0) == componentType &&642type->getIdOperand(1) == components)643return type->getResultId();644}645646// not found, make it647type = new Instruction(getUniqueId(), NoType, Op::OpTypeCooperativeVectorNV);648type->addIdOperand(componentType);649type->addIdOperand(components);650groupedTypes[enumCast(Op::OpTypeCooperativeVectorNV)].push_back(type);651constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));652module.mapInstruction(type);653654return type->getResultId();655}656657Id Builder::makeTensorTypeARM(Id elementType, Id rank)658{659// See if an OpTypeTensorARM with same element type and rank already exists.660for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeTensorARM)].size(); ++t) {661const Instruction *type = groupedTypes[enumCast(Op::OpTypeTensorARM)][t];662if (type->getIdOperand(0) == elementType && type->getIdOperand(1) == rank)663return type->getResultId();664}665666// Not found, make it.667std::unique_ptr<Instruction> type(new Instruction(getUniqueId(), NoType, Op::OpTypeTensorARM));668type->addIdOperand(elementType);669type->addIdOperand(rank);670groupedTypes[enumCast(Op::OpTypeTensorARM)].push_back(type.get());671module.mapInstruction(type.get());672Id resultID = type->getResultId();673constantsTypesGlobals.push_back(std::move(type));674return resultID;675}676677Id Builder::makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands)678{679// try to find it680Instruction* type;681for (int t = 0; t < (int)groupedTypes[enumCast(opcode)].size(); ++t) {682type = groupedTypes[enumCast(opcode)][t];683if (static_cast<size_t>(type->getNumOperands()) != operands.size())684continue; // Number mismatch, find next685686bool match = true;687for (int op = 0; match && op < (int)operands.size(); ++op) {688match = (operands[op].isId ? type->getIdOperand(op) : type->getImmediateOperand(op)) == operands[op].word;689}690if (match)691return type->getResultId();692}693694// not found, make it695type = new Instruction(getUniqueId(), NoType, opcode);696type->reserveOperands(operands.size());697for (size_t op = 0; op < operands.size(); ++op) {698if (operands[op].isId)699type->addIdOperand(operands[op].word);700else701type->addImmediateOperand(operands[op].word);702}703groupedTypes[enumCast(opcode)].push_back(type);704constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));705module.mapInstruction(type);706707return type->getResultId();708}709710// TODO: performance: track arrays per stride711// If a stride is supplied (non-zero) make an array.712// If no stride (0), reuse previous array types.713// 'size' is an Id of a constant or specialization constant of the array size714Id Builder::makeArrayType(Id element, Id sizeId, int stride)715{716Instruction* type;717if (stride == 0) {718// try to find existing type719for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeArray)].size(); ++t) {720type = groupedTypes[enumCast(Op::OpTypeArray)][t];721if (type->getIdOperand(0) == element &&722type->getIdOperand(1) == sizeId &&723explicitlyLaidOut.find(type->getResultId()) == explicitlyLaidOut.end())724return type->getResultId();725}726}727728// not found, make it729type = new Instruction(getUniqueId(), NoType, Op::OpTypeArray);730type->reserveOperands(2);731type->addIdOperand(element);732type->addIdOperand(sizeId);733groupedTypes[enumCast(Op::OpTypeArray)].push_back(type);734constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));735module.mapInstruction(type);736737if (stride != 0) {738explicitlyLaidOut.insert(type->getResultId());739}740741if (emitNonSemanticShaderDebugInfo)742{743auto const debugResultId = makeArrayDebugType(element, sizeId);744debugTypeIdLookup[type->getResultId()] = debugResultId;745}746747return type->getResultId();748}749750Id Builder::makeRuntimeArray(Id element)751{752Instruction* type = new Instruction(getUniqueId(), NoType, Op::OpTypeRuntimeArray);753type->addIdOperand(element);754constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));755module.mapInstruction(type);756757if (emitNonSemanticShaderDebugInfo)758{759auto const debugResultId = makeArrayDebugType(element, makeUintConstant(0));760debugTypeIdLookup[type->getResultId()] = debugResultId;761}762763return type->getResultId();764}765766Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes)767{768// try to find it769Instruction* type;770for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeFunction)].size(); ++t) {771type = groupedTypes[enumCast(Op::OpTypeFunction)][t];772if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1)773continue;774bool mismatch = false;775for (int p = 0; p < (int)paramTypes.size(); ++p) {776if (paramTypes[p] != type->getIdOperand(p + 1)) {777mismatch = true;778break;779}780}781if (! mismatch)782{783// If compiling HLSL, glslang will create a wrapper function around the entrypoint. Accordingly, a void(void)784// function type is created for the wrapper function. However, nonsemantic shader debug information is disabled785// while creating the HLSL wrapper. Consequently, if we encounter another void(void) function, we need to create786// the associated debug function type if it hasn't been created yet.787if(emitNonSemanticShaderDebugInfo && getDebugType(type->getResultId()) == NoType) {788assert(sourceLang == spv::SourceLanguage::HLSL);789assert(getTypeClass(returnType) == Op::OpTypeVoid && paramTypes.size() == 0);790791Id id = makeDebugFunctionType(returnType, {});792debugTypeIdLookup[type->getResultId()] = id;793}794return type->getResultId();795}796}797798// not found, make it799Id typeId = getUniqueId();800type = new Instruction(typeId, NoType, Op::OpTypeFunction);801type->reserveOperands(paramTypes.size() + 1);802type->addIdOperand(returnType);803for (int p = 0; p < (int)paramTypes.size(); ++p)804type->addIdOperand(paramTypes[p]);805groupedTypes[enumCast(Op::OpTypeFunction)].push_back(type);806constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));807module.mapInstruction(type);808809// make debug type and map it810if (emitNonSemanticShaderDebugInfo) {811Id debugTypeId = makeDebugFunctionType(returnType, paramTypes);812debugTypeIdLookup[typeId] = debugTypeId;813}814815return type->getResultId();816}817818Id Builder::makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes)819{820assert(getDebugType(returnType) != NoType);821822Id typeId = getUniqueId();823auto type = new Instruction(typeId, makeVoidType(), Op::OpExtInst);824type->reserveOperands(paramTypes.size() + 4);825type->addIdOperand(nonSemanticShaderDebugInfo);826type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeFunction);827type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic));828type->addIdOperand(getDebugType(returnType));829for (auto const paramType : paramTypes) {830if (isPointerType(paramType) || isArrayType(paramType)) {831type->addIdOperand(getDebugType(getContainedTypeId(paramType)));832}833else {834type->addIdOperand(getDebugType(paramType));835}836}837constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));838module.mapInstruction(type);839return typeId;840}841842Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled,843ImageFormat format, const char* debugName)844{845assert(sampled == 1 || sampled == 2);846847// try to find it848Instruction* type;849for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeImage)].size(); ++t) {850type = groupedTypes[enumCast(Op::OpTypeImage)][t];851if (type->getIdOperand(0) == sampledType &&852type->getImmediateOperand(1) == (unsigned int)dim &&853type->getImmediateOperand(2) == ( depth ? 1u : 0u) &&854type->getImmediateOperand(3) == (arrayed ? 1u : 0u) &&855type->getImmediateOperand(4) == ( ms ? 1u : 0u) &&856type->getImmediateOperand(5) == sampled &&857type->getImmediateOperand(6) == (unsigned int)format)858return type->getResultId();859}860861// not found, make it862type = new Instruction(getUniqueId(), NoType, Op::OpTypeImage);863type->reserveOperands(7);864type->addIdOperand(sampledType);865type->addImmediateOperand( dim);866type->addImmediateOperand( depth ? 1 : 0);867type->addImmediateOperand(arrayed ? 1 : 0);868type->addImmediateOperand( ms ? 1 : 0);869type->addImmediateOperand(sampled);870type->addImmediateOperand((unsigned int)format);871872groupedTypes[enumCast(Op::OpTypeImage)].push_back(type);873constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));874module.mapInstruction(type);875876// deal with capabilities877switch (dim) {878case Dim::Buffer:879if (sampled == 1)880addCapability(Capability::SampledBuffer);881else882addCapability(Capability::ImageBuffer);883break;884case Dim::Dim1D:885if (sampled == 1)886addCapability(Capability::Sampled1D);887else888addCapability(Capability::Image1D);889break;890case Dim::Cube:891if (arrayed) {892if (sampled == 1)893addCapability(Capability::SampledCubeArray);894else895addCapability(Capability::ImageCubeArray);896}897break;898case Dim::Rect:899if (sampled == 1)900addCapability(Capability::SampledRect);901else902addCapability(Capability::ImageRect);903break;904case Dim::SubpassData:905addCapability(Capability::InputAttachment);906break;907default:908break;909}910911if (ms) {912if (sampled == 2) {913// Images used with subpass data are not storage914// images, so don't require the capability for them.915if (dim != Dim::SubpassData)916addCapability(Capability::StorageImageMultisample);917if (arrayed)918addCapability(Capability::ImageMSArray);919}920}921922if (emitNonSemanticShaderDebugInfo)923{924auto const debugResultId = makeOpaqueDebugType(debugName);925debugTypeIdLookup[type->getResultId()] = debugResultId;926}927928return type->getResultId();929}930931Id Builder::makeSampledImageType(Id imageType, const char* debugName)932{933// try to find it934Instruction* type;935for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeSampledImage)].size(); ++t) {936type = groupedTypes[enumCast(Op::OpTypeSampledImage)][t];937if (type->getIdOperand(0) == imageType)938return type->getResultId();939}940941// not found, make it942type = new Instruction(getUniqueId(), NoType, Op::OpTypeSampledImage);943type->addIdOperand(imageType);944945groupedTypes[enumCast(Op::OpTypeSampledImage)].push_back(type);946constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));947module.mapInstruction(type);948949if (emitNonSemanticShaderDebugInfo)950{951auto const debugResultId = makeOpaqueDebugType(debugName);952debugTypeIdLookup[type->getResultId()] = debugResultId;953}954955return type->getResultId();956}957958Id Builder::makeDebugInfoNone()959{960if (debugInfoNone != 0)961return debugInfoNone;962963Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);964inst->reserveOperands(2);965inst->addIdOperand(nonSemanticShaderDebugInfo);966inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugInfoNone);967968constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));969module.mapInstruction(inst);970971debugInfoNone = inst->getResultId();972973return debugInfoNone;974}975976Id Builder::makeBoolDebugType(int const size)977{978// try to find it979Instruction* type;980for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) {981type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t];982if (type->getIdOperand(0) == getStringId("bool") &&983type->getIdOperand(1) == static_cast<unsigned int>(size) &&984type->getIdOperand(2) == NonSemanticShaderDebugInfo100Boolean)985return type->getResultId();986}987988type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);989type->reserveOperands(6);990type->addIdOperand(nonSemanticShaderDebugInfo);991type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic);992993type->addIdOperand(getStringId("bool")); // name id994type->addIdOperand(makeUintConstant(size)); // size id995type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Boolean)); // encoding id996type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id997998groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type);999constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1000module.mapInstruction(type);10011002return type->getResultId();1003}10041005Id Builder::makeIntegerDebugType(int const width, bool const hasSign)1006{1007const char* typeName = nullptr;1008switch (width) {1009case 8: typeName = hasSign ? "int8_t" : "uint8_t"; break;1010case 16: typeName = hasSign ? "int16_t" : "uint16_t"; break;1011case 64: typeName = hasSign ? "int64_t" : "uint64_t"; break;1012default: typeName = hasSign ? "int" : "uint";1013}1014auto nameId = getStringId(typeName);1015// try to find it1016Instruction* type;1017for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) {1018type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t];1019if (type->getIdOperand(0) == nameId &&1020type->getIdOperand(1) == static_cast<unsigned int>(width) &&1021type->getIdOperand(2) == (hasSign ? NonSemanticShaderDebugInfo100Signed : NonSemanticShaderDebugInfo100Unsigned))1022return type->getResultId();1023}10241025// not found, make it1026type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1027type->reserveOperands(6);1028type->addIdOperand(nonSemanticShaderDebugInfo);1029type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic);1030type->addIdOperand(nameId); // name id1031type->addIdOperand(makeUintConstant(width)); // size id1032if(hasSign == true) {1033type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Signed)); // encoding id1034} else {1035type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Unsigned)); // encoding id1036}1037type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id10381039groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type);1040constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1041module.mapInstruction(type);10421043return type->getResultId();1044}10451046Id Builder::makeFloatDebugType(int const width)1047{1048const char* typeName = nullptr;1049switch (width) {1050case 16: typeName = "float16_t"; break;1051case 64: typeName = "double"; break;1052default: typeName = "float"; break;1053}1054auto nameId = getStringId(typeName);1055// try to find it1056Instruction* type;1057for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) {1058type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t];1059if (type->getIdOperand(0) == nameId &&1060type->getIdOperand(1) == static_cast<unsigned int>(width) &&1061type->getIdOperand(2) == NonSemanticShaderDebugInfo100Float)1062return type->getResultId();1063}10641065// not found, make it1066type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1067type->reserveOperands(6);1068type->addIdOperand(nonSemanticShaderDebugInfo);1069type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic);1070type->addIdOperand(nameId); // name id1071type->addIdOperand(makeUintConstant(width)); // size id1072type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Float)); // encoding id1073type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id10741075groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type);1076constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1077module.mapInstruction(type);10781079return type->getResultId();1080}10811082Id Builder::makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfo100Instructions const sequenceType)1083{1084assert(sequenceType == NonSemanticShaderDebugInfo100DebugTypeArray ||1085sequenceType == NonSemanticShaderDebugInfo100DebugTypeVector);10861087// try to find it1088Instruction* type;1089for (int t = 0; t < (int)groupedDebugTypes[sequenceType].size(); ++t) {1090type = groupedDebugTypes[sequenceType][t];1091if (type->getIdOperand(0) == baseType &&1092type->getIdOperand(1) == makeUintConstant(componentCount))1093return type->getResultId();1094}10951096// not found, make it1097type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1098type->reserveOperands(4);1099type->addIdOperand(nonSemanticShaderDebugInfo);1100type->addImmediateOperand(sequenceType);1101type->addIdOperand(getDebugType(baseType)); // base type1102type->addIdOperand(componentCount); // component count11031104groupedDebugTypes[sequenceType].push_back(type);1105constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1106module.mapInstruction(type);11071108return type->getResultId();1109}11101111Id Builder::makeArrayDebugType(Id const baseType, Id const componentCount)1112{1113return makeSequentialDebugType(baseType, componentCount, NonSemanticShaderDebugInfo100DebugTypeArray);1114}11151116Id Builder::makeVectorDebugType(Id const baseType, int const componentCount)1117{1118return makeSequentialDebugType(baseType, makeUintConstant(componentCount), NonSemanticShaderDebugInfo100DebugTypeVector);1119}11201121Id Builder::makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor)1122{1123// try to find it1124Instruction* type;1125for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].size(); ++t) {1126type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix][t];1127if (type->getIdOperand(0) == vectorType &&1128type->getIdOperand(1) == makeUintConstant(vectorCount))1129return type->getResultId();1130}11311132// not found, make it1133type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1134type->reserveOperands(5);1135type->addIdOperand(nonSemanticShaderDebugInfo);1136type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeMatrix);1137type->addIdOperand(getDebugType(vectorType)); // vector type id1138type->addIdOperand(makeUintConstant(vectorCount)); // component count id1139type->addIdOperand(makeBoolConstant(columnMajor)); // column-major id11401141groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].push_back(type);1142constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1143module.mapInstruction(type);11441145return type->getResultId();1146}11471148Id Builder::makeMemberDebugType(Id const memberType, StructMemberDebugInfo const& debugTypeLoc)1149{1150assert(getDebugType(memberType) != NoType);11511152Instruction* type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1153type->reserveOperands(10);1154type->addIdOperand(nonSemanticShaderDebugInfo);1155type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeMember);1156type->addIdOperand(getStringId(debugTypeLoc.name)); // name id1157type->addIdOperand(debugTypeLoc.debugTypeOverride != 0 ? debugTypeLoc.debugTypeOverride1158: getDebugType(memberType)); // type id1159type->addIdOperand(makeDebugSource(currentFileId)); // source id1160type->addIdOperand(makeUintConstant(debugTypeLoc.line)); // line id TODO: currentLine is always zero1161type->addIdOperand(makeUintConstant(debugTypeLoc.column)); // TODO: column id1162type->addIdOperand(makeUintConstant(0)); // TODO: offset id1163type->addIdOperand(makeUintConstant(0)); // TODO: size id1164type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id11651166groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMember].push_back(type);1167constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1168module.mapInstruction(type);11691170return type->getResultId();1171}11721173Id Builder::makeCompositeDebugType(std::vector<Id> const& memberTypes, std::vector<StructMemberDebugInfo> const& memberDebugInfo,1174char const* const name, NonSemanticShaderDebugInfo100DebugCompositeType const tag)1175{1176// Create the debug member types.1177std::vector<Id> memberDebugTypes;1178assert(memberTypes.size() == memberDebugInfo.size());1179for (size_t i = 0; i < memberTypes.size(); i++) {1180if (getDebugType(memberTypes[i]) != NoType) {1181memberDebugTypes.emplace_back(makeMemberDebugType(memberTypes[i], memberDebugInfo[i]));1182}1183}11841185// Create The structure debug type.1186Instruction* type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1187type->reserveOperands(memberDebugTypes.size() + 11);1188type->addIdOperand(nonSemanticShaderDebugInfo);1189type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeComposite);1190type->addIdOperand(getStringId(name)); // name id1191type->addIdOperand(makeUintConstant(tag)); // tag id1192type->addIdOperand(makeDebugSource(currentFileId)); // source id1193type->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero?1194type->addIdOperand(makeUintConstant(0)); // TODO: column id1195type->addIdOperand(makeDebugCompilationUnit()); // scope id1196type->addIdOperand(getStringId(name)); // linkage name id1197type->addIdOperand(makeUintConstant(0)); // TODO: size id1198type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id1199for(auto const memberDebugType : memberDebugTypes) {1200type->addIdOperand(memberDebugType);1201}12021203groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeComposite].push_back(type);1204constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1205module.mapInstruction(type);12061207return type->getResultId();1208}12091210// The NonSemantic Shader Debug Info doesn't really have a dedicated opcode for opaque types. Instead, we use DebugTypeComposite.1211// To represent a source language opaque type, this instruction must have no Members operands, Size operand must be1212// DebugInfoNone, and Name must start with @ to avoid clashes with user defined names.1213Id Builder::makeOpaqueDebugType(char const* const name)1214{1215// Create The structure debug type.1216Instruction* type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1217type->reserveOperands(11);1218type->addIdOperand(nonSemanticShaderDebugInfo);1219type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeComposite);1220type->addIdOperand(getStringId(name)); // name id1221type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Structure)); // tag id1222type->addIdOperand(makeDebugSource(currentFileId)); // source id1223type->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero?1224type->addIdOperand(makeUintConstant(0)); // TODO: column id1225type->addIdOperand(makeDebugCompilationUnit()); // scope id1226// Prepend '@' to opaque types.1227type->addIdOperand(getStringId('@' + std::string(name))); // linkage name id1228type->addIdOperand(makeDebugInfoNone()); // size id1229type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id12301231groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeComposite].push_back(type);1232constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1233module.mapInstruction(type);12341235return type->getResultId();1236}12371238Id Builder::makePointerDebugType(StorageClass storageClass, Id const baseType)1239{1240const Id debugBaseType = getDebugType(baseType);1241if (!debugBaseType) {1242return makeDebugInfoNone();1243}1244const Id scID = makeUintConstant(storageClass);1245for (Instruction* otherType : groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypePointer]) {1246if (otherType->getIdOperand(2) == debugBaseType &&1247otherType->getIdOperand(3) == scID) {1248return otherType->getResultId();1249}1250}12511252Instruction* type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1253type->reserveOperands(5);1254type->addIdOperand(nonSemanticShaderDebugInfo);1255type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypePointer);1256type->addIdOperand(debugBaseType);1257type->addIdOperand(scID);1258type->addIdOperand(makeUintConstant(0));12591260groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypePointer].push_back(type);1261constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1262module.mapInstruction(type);12631264return type->getResultId();1265}12661267// Emit a OpExtInstWithForwardRefsKHR nonsemantic instruction for a pointer debug type1268// where we don't have the pointee yet. Since we don't have the pointee yet, it just1269// points to itself and we rely on patching it later.1270Id Builder::makeForwardPointerDebugType(StorageClass storageClass)1271{1272const Id scID = makeUintConstant(storageClass);12731274this->addExtension(spv::E_SPV_KHR_relaxed_extended_instruction);12751276Instruction *type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInstWithForwardRefsKHR);1277type->addIdOperand(nonSemanticShaderDebugInfo);1278type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypePointer);1279type->addIdOperand(type->getResultId());1280type->addIdOperand(scID);1281type->addIdOperand(makeUintConstant(0));12821283groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypePointer].push_back(type);1284constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1285module.mapInstruction(type);12861287return type->getResultId();1288}12891290Id Builder::makeDebugSource(const Id fileName) {1291if (debugSourceId.find(fileName) != debugSourceId.end())1292return debugSourceId[fileName];1293spv::Id resultId = getUniqueId();1294Instruction* sourceInst = new Instruction(resultId, makeVoidType(), Op::OpExtInst);1295sourceInst->reserveOperands(3);1296sourceInst->addIdOperand(nonSemanticShaderDebugInfo);1297sourceInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugSource);1298sourceInst->addIdOperand(fileName);1299constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceInst));1300module.mapInstruction(sourceInst);1301if (emitNonSemanticShaderDebugSource) {1302const int maxWordCount = 0xFFFF;1303const int opSourceWordCount = 4;1304const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1;1305auto processDebugSource = [&](std::string source) {1306if (source.size() > 0) {1307int nextByte = 0;1308while ((int)source.size() - nextByte > 0) {1309auto subString = source.substr(nextByte, nonNullBytesPerInstruction);1310auto sourceId = getStringId(subString);1311if (nextByte == 0) {1312// DebugSource1313sourceInst->addIdOperand(sourceId);1314} else {1315// DebugSourceContinued1316Instruction* sourceContinuedInst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1317sourceContinuedInst->reserveOperands(2);1318sourceContinuedInst->addIdOperand(nonSemanticShaderDebugInfo);1319sourceContinuedInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugSourceContinued);1320sourceContinuedInst->addIdOperand(sourceId);1321constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceContinuedInst));1322module.mapInstruction(sourceContinuedInst);1323}1324nextByte += nonNullBytesPerInstruction;1325}1326} else {1327auto sourceId = getStringId(source);1328sourceInst->addIdOperand(sourceId);1329}1330};1331if (fileName == mainFileId) {1332processDebugSource(sourceText);1333} else {1334auto incItr = includeFiles.find(fileName);1335if (incItr != includeFiles.end()) {1336processDebugSource(*incItr->second);1337} else {1338// We omit the optional source text item if not available in glslang1339}1340}1341}1342debugSourceId[fileName] = resultId;1343return resultId;1344}13451346Id Builder::makeDebugCompilationUnit() {1347if (nonSemanticShaderCompilationUnitId != 0)1348return nonSemanticShaderCompilationUnitId;1349spv::Id resultId = getUniqueId();1350Instruction* sourceInst = new Instruction(resultId, makeVoidType(), Op::OpExtInst);1351sourceInst->reserveOperands(6);1352sourceInst->addIdOperand(nonSemanticShaderDebugInfo);1353sourceInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugCompilationUnit);1354sourceInst->addIdOperand(makeUintConstant(1)); // TODO(greg-lunarg): Get rid of magic number1355sourceInst->addIdOperand(makeUintConstant(4)); // TODO(greg-lunarg): Get rid of magic number1356sourceInst->addIdOperand(makeDebugSource(mainFileId));1357sourceInst->addIdOperand(makeUintConstant(sourceLang));1358constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceInst));1359module.mapInstruction(sourceInst);1360nonSemanticShaderCompilationUnitId = resultId;13611362// We can reasonably assume that makeDebugCompilationUnit will be called before any of1363// debug-scope stack. Function scopes and lexical scopes will occur afterward.1364assert(currentDebugScopeId.empty());1365currentDebugScopeId.push(nonSemanticShaderCompilationUnitId);13661367return resultId;1368}13691370Id Builder::createDebugGlobalVariable(Id const type, char const*const name, Id const variable)1371{1372assert(type != 0);13731374Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1375inst->reserveOperands(11);1376inst->addIdOperand(nonSemanticShaderDebugInfo);1377inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugGlobalVariable);1378inst->addIdOperand(getStringId(name)); // name id1379inst->addIdOperand(type); // type id1380inst->addIdOperand(makeDebugSource(currentFileId)); // source id1381inst->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero?1382inst->addIdOperand(makeUintConstant(0)); // TODO: column id1383inst->addIdOperand(makeDebugCompilationUnit()); // scope id1384inst->addIdOperand(getStringId(name)); // linkage name id1385inst->addIdOperand(variable); // variable id1386inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsDefinition)); // flags id13871388constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));1389module.mapInstruction(inst);13901391return inst->getResultId();1392}13931394Id Builder::createDebugLocalVariable(Id type, char const*const name, size_t const argNumber)1395{1396assert(name != nullptr);1397assert(!currentDebugScopeId.empty());13981399Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1400inst->reserveOperands(9);1401inst->addIdOperand(nonSemanticShaderDebugInfo);1402inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLocalVariable);1403inst->addIdOperand(getStringId(name)); // name id1404inst->addIdOperand(type); // type id1405inst->addIdOperand(makeDebugSource(currentFileId)); // source id1406inst->addIdOperand(makeUintConstant(currentLine)); // line id1407inst->addIdOperand(makeUintConstant(0)); // TODO: column id1408inst->addIdOperand(currentDebugScopeId.top()); // scope id1409inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsLocal)); // flags id1410if(argNumber != 0) {1411inst->addIdOperand(makeUintConstant(static_cast<unsigned int>(argNumber)));1412}14131414constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));1415module.mapInstruction(inst);14161417return inst->getResultId();1418}14191420Id Builder::makeDebugExpression()1421{1422if (debugExpression != 0)1423return debugExpression;14241425Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1426inst->reserveOperands(2);1427inst->addIdOperand(nonSemanticShaderDebugInfo);1428inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugExpression);14291430constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));1431module.mapInstruction(inst);14321433debugExpression = inst->getResultId();14341435return debugExpression;1436}14371438Id Builder::makeDebugDeclare(Id const debugLocalVariable, Id const pointer)1439{1440Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1441inst->reserveOperands(5);1442inst->addIdOperand(nonSemanticShaderDebugInfo);1443inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugDeclare);1444inst->addIdOperand(debugLocalVariable); // debug local variable id1445inst->addIdOperand(pointer); // pointer to local variable id1446inst->addIdOperand(makeDebugExpression()); // expression id1447addInstruction(std::unique_ptr<Instruction>(inst));14481449return inst->getResultId();1450}14511452Id Builder::makeDebugValue(Id const debugLocalVariable, Id const value)1453{1454Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst);1455inst->reserveOperands(5);1456inst->addIdOperand(nonSemanticShaderDebugInfo);1457inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugValue);1458inst->addIdOperand(debugLocalVariable); // debug local variable id1459inst->addIdOperand(value); // value of local variable id1460inst->addIdOperand(makeDebugExpression()); // expression id1461addInstruction(std::unique_ptr<Instruction>(inst));14621463return inst->getResultId();1464}14651466Id Builder::makeAccelerationStructureType()1467{1468Instruction *type;1469if (groupedTypes[enumCast(Op::OpTypeAccelerationStructureKHR)].size() == 0) {1470type = new Instruction(getUniqueId(), NoType, Op::OpTypeAccelerationStructureKHR);1471groupedTypes[enumCast(Op::OpTypeAccelerationStructureKHR)].push_back(type);1472constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1473module.mapInstruction(type);1474if (emitNonSemanticShaderDebugInfo) {1475spv::Id debugType = makeOpaqueDebugType("accelerationStructure");1476debugTypeIdLookup[type->getResultId()] = debugType;1477}1478} else {1479type = groupedTypes[enumCast(Op::OpTypeAccelerationStructureKHR)].back();1480}14811482return type->getResultId();1483}14841485Id Builder::makeRayQueryType()1486{1487Instruction *type;1488if (groupedTypes[enumCast(Op::OpTypeRayQueryKHR)].size() == 0) {1489type = new Instruction(getUniqueId(), NoType, Op::OpTypeRayQueryKHR);1490groupedTypes[enumCast(Op::OpTypeRayQueryKHR)].push_back(type);1491constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1492module.mapInstruction(type);1493if (emitNonSemanticShaderDebugInfo) {1494spv::Id debugType = makeOpaqueDebugType("rayQuery");1495debugTypeIdLookup[type->getResultId()] = debugType;1496}1497} else {1498type = groupedTypes[enumCast(Op::OpTypeRayQueryKHR)].back();1499}15001501return type->getResultId();1502}15031504Id Builder::makeHitObjectEXTType()1505{1506Instruction *type;1507if (groupedTypes[enumCast(Op::OpTypeHitObjectEXT)].size() == 0) {1508type = new Instruction(getUniqueId(), NoType, Op::OpTypeHitObjectEXT);1509groupedTypes[enumCast(Op::OpTypeHitObjectEXT)].push_back(type);1510constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1511module.mapInstruction(type);1512} else {1513type = groupedTypes[enumCast(Op::OpTypeHitObjectEXT)].back();1514}15151516return type->getResultId();1517}1518Id Builder::makeHitObjectNVType()1519{1520Instruction *type;1521if (groupedTypes[enumCast(Op::OpTypeHitObjectNV)].size() == 0) {1522type = new Instruction(getUniqueId(), NoType, Op::OpTypeHitObjectNV);1523groupedTypes[enumCast(Op::OpTypeHitObjectNV)].push_back(type);1524constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1525module.mapInstruction(type);1526if (emitNonSemanticShaderDebugInfo) {1527spv::Id debugType = makeOpaqueDebugType("hitObjectNV");1528debugTypeIdLookup[type->getResultId()] = debugType;1529}1530} else {1531type = groupedTypes[enumCast(Op::OpTypeHitObjectNV)].back();1532}15331534return type->getResultId();1535}15361537Id Builder::getDerefTypeId(Id resultId) const1538{1539Id typeId = getTypeId(resultId);1540assert(isPointerType(typeId));15411542return module.getInstruction(typeId)->getIdOperand(1);1543}15441545Op Builder::getMostBasicTypeClass(Id typeId) const1546{1547Instruction* instr = module.getInstruction(typeId);15481549Op typeClass = instr->getOpCode();1550switch (typeClass)1551{1552case Op::OpTypeVector:1553case Op::OpTypeMatrix:1554case Op::OpTypeArray:1555case Op::OpTypeRuntimeArray:1556return getMostBasicTypeClass(instr->getIdOperand(0));1557case Op::OpTypePointer:1558return getMostBasicTypeClass(instr->getIdOperand(1));1559default:1560return typeClass;1561}1562}15631564unsigned int Builder::getNumTypeConstituents(Id typeId) const1565{1566Instruction* instr = module.getInstruction(typeId);15671568switch (instr->getOpCode())1569{1570case Op::OpTypeBool:1571case Op::OpTypeInt:1572case Op::OpTypeFloat:1573case Op::OpTypePointer:1574return 1;1575case Op::OpTypeVector:1576case Op::OpTypeMatrix:1577return instr->getImmediateOperand(1);1578case Op::OpTypeCooperativeVectorNV:1579case Op::OpTypeArray:1580{1581Id lengthId = instr->getIdOperand(1);1582return module.getInstruction(lengthId)->getImmediateOperand(0);1583}1584case Op::OpTypeStruct:1585return instr->getNumOperands();1586case Op::OpTypeCooperativeMatrixKHR:1587case Op::OpTypeCooperativeMatrixNV:1588// has only one constituent when used with OpCompositeConstruct.1589return 1;1590default:1591assert(0);1592return 1;1593}1594}15951596// Return the lowest-level type of scalar that an homogeneous composite is made out of.1597// Typically, this is just to find out if something is made out of ints or floats.1598// However, it includes returning a structure, if say, it is an array of structure.1599Id Builder::getScalarTypeId(Id typeId) const1600{1601Instruction* instr = module.getInstruction(typeId);16021603Op typeClass = instr->getOpCode();1604switch (typeClass)1605{1606case Op::OpTypeVoid:1607case Op::OpTypeBool:1608case Op::OpTypeInt:1609case Op::OpTypeFloat:1610case Op::OpTypeStruct:1611return instr->getResultId();1612case Op::OpTypeVector:1613case Op::OpTypeMatrix:1614case Op::OpTypeArray:1615case Op::OpTypeRuntimeArray:1616case Op::OpTypePointer:1617case Op::OpTypeCooperativeVectorNV:1618return getScalarTypeId(getContainedTypeId(typeId));1619default:1620assert(0);1621return NoResult;1622}1623}16241625// Return the type of 'member' of a composite.1626Id Builder::getContainedTypeId(Id typeId, int member) const1627{1628Instruction* instr = module.getInstruction(typeId);16291630Op typeClass = instr->getOpCode();1631switch (typeClass)1632{1633case Op::OpTypeVector:1634case Op::OpTypeMatrix:1635case Op::OpTypeArray:1636case Op::OpTypeRuntimeArray:1637case Op::OpTypeCooperativeMatrixKHR:1638case Op::OpTypeCooperativeMatrixNV:1639case Op::OpTypeCooperativeVectorNV:1640return instr->getIdOperand(0);1641case Op::OpTypePointer:1642return instr->getIdOperand(1);1643case Op::OpTypeStruct:1644return instr->getIdOperand(member);1645default:1646assert(0);1647return NoResult;1648}1649}16501651// Figure out the final resulting type of the access chain.1652Id Builder::getResultingAccessChainType() const1653{1654assert(accessChain.base != NoResult);1655Id typeId = getTypeId(accessChain.base);16561657assert(isPointerType(typeId));1658typeId = getContainedTypeId(typeId);16591660for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {1661if (isStructType(typeId)) {1662assert(isConstantScalar(accessChain.indexChain[i]));1663typeId = getContainedTypeId(typeId, getConstantScalar(accessChain.indexChain[i]));1664} else1665typeId = getContainedTypeId(typeId, accessChain.indexChain[i]);1666}16671668return typeId;1669}16701671// Return the immediately contained type of a given composite type.1672Id Builder::getContainedTypeId(Id typeId) const1673{1674return getContainedTypeId(typeId, 0);1675}16761677// Returns true if 'typeId' is or contains a scalar type declared with 'typeOp'1678// of width 'width'. The 'width' is only consumed for int and float types.1679// Returns false otherwise.1680bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const1681{1682const Instruction& instr = *module.getInstruction(typeId);16831684Op typeClass = instr.getOpCode();1685switch (typeClass)1686{1687case Op::OpTypeInt:1688case Op::OpTypeFloat:1689return typeClass == typeOp && instr.getImmediateOperand(0) == width;1690case Op::OpTypeStruct:1691for (int m = 0; m < instr.getNumOperands(); ++m) {1692if (containsType(instr.getIdOperand(m), typeOp, width))1693return true;1694}1695return false;1696case Op::OpTypePointer:1697return false;1698case Op::OpTypeVector:1699case Op::OpTypeMatrix:1700case Op::OpTypeArray:1701case Op::OpTypeRuntimeArray:1702return containsType(getContainedTypeId(typeId), typeOp, width);1703default:1704return typeClass == typeOp;1705}1706}17071708// return true if the type is a pointer to PhysicalStorageBufferEXT or an1709// contains such a pointer. These require restrict/aliased decorations.1710bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const1711{1712const Instruction& instr = *module.getInstruction(typeId);17131714Op typeClass = instr.getOpCode();1715switch (typeClass)1716{1717case Op::OpTypePointer:1718return getTypeStorageClass(typeId) == StorageClass::PhysicalStorageBufferEXT;1719case Op::OpTypeArray:1720return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId));1721case Op::OpTypeStruct:1722for (int m = 0; m < instr.getNumOperands(); ++m) {1723if (containsPhysicalStorageBufferOrArray(instr.getIdOperand(m)))1724return true;1725}1726return false;1727default:1728return false;1729}1730}17311732// See if a scalar constant of this type has already been created, so it1733// can be reused rather than duplicated. (Required by the specification).1734Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value)1735{1736ScalarConstantKey key{ enumCast(typeClass), enumCast(opcode), typeId, value, 0 };1737auto it = groupedScalarConstantResultIDs.find(key);1738return (it != groupedScalarConstantResultIDs.end()) ? it->second : 0;1739}17401741// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64').1742Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2)1743{1744ScalarConstantKey key{ enumCast(typeClass), enumCast(opcode), typeId, v1, v2 };1745auto it = groupedScalarConstantResultIDs.find(key);1746return (it != groupedScalarConstantResultIDs.end()) ? it->second : 0;1747}17481749// Return true if consuming 'opcode' means consuming a constant.1750// "constant" here means after final transform to executable code,1751// the value consumed will be a constant, so includes specialization.1752bool Builder::isConstantOpCode(Op opcode) const1753{1754switch (opcode) {1755case Op::OpUndef:1756case Op::OpConstantTrue:1757case Op::OpConstantFalse:1758case Op::OpConstant:1759case Op::OpConstantComposite:1760case Op::OpConstantCompositeReplicateEXT:1761case Op::OpConstantSampler:1762case Op::OpConstantNull:1763case Op::OpSpecConstantTrue:1764case Op::OpSpecConstantFalse:1765case Op::OpSpecConstant:1766case Op::OpSpecConstantComposite:1767case Op::OpSpecConstantCompositeReplicateEXT:1768case Op::OpSpecConstantOp:1769return true;1770default:1771return false;1772}1773}17741775// Return true if consuming 'opcode' means consuming a specialization constant.1776bool Builder::isSpecConstantOpCode(Op opcode) const1777{1778switch (opcode) {1779case Op::OpSpecConstantTrue:1780case Op::OpSpecConstantFalse:1781case Op::OpSpecConstant:1782case Op::OpSpecConstantComposite:1783case Op::OpSpecConstantOp:1784case Op::OpSpecConstantCompositeReplicateEXT:1785return true;1786default:1787return false;1788}1789}17901791Id Builder::makeNullConstant(Id typeId)1792{1793Instruction* constant;17941795// See if we already made it.1796Id existing = NoResult;1797for (int i = 0; i < (int)nullConstants.size(); ++i) {1798constant = nullConstants[i];1799if (constant->getTypeId() == typeId)1800existing = constant->getResultId();1801}18021803if (existing != NoResult)1804return existing;18051806// Make it1807Instruction* c = new Instruction(getUniqueId(), typeId, Op::OpConstantNull);1808constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1809nullConstants.push_back(c);1810module.mapInstruction(c);18111812return c->getResultId();1813}18141815Id Builder::makeBoolConstant(bool b, bool specConstant)1816{1817Id typeId = makeBoolType();1818Op opcode = specConstant ? (b ? Op::OpSpecConstantTrue : Op::OpSpecConstantFalse) : (b ? Op::OpConstantTrue : Op::OpConstantFalse);18191820// See if we already made it. Applies only to regular constants, because specialization constants1821// must remain distinct for the purpose of applying a SpecId decoration.1822if (!specConstant) {1823Id existing = findScalarConstant(Op::OpTypeBool, opcode, typeId, 0);1824if (existing)1825return existing;1826}18271828// Make it1829Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1830constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1831module.mapInstruction(c);18321833Id resultId = c->getResultId();1834if (!specConstant) {1835ScalarConstantKey key{enumCast(Op::OpTypeBool), enumCast(opcode), typeId, 0, 0};1836groupedScalarConstantResultIDs[key] = resultId;1837}1838return resultId;1839}18401841Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant)1842{1843Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant;18441845// See if we already made it. Applies only to regular constants, because specialization constants1846// must remain distinct for the purpose of applying a SpecId decoration.1847if (! specConstant) {1848Id existing = findScalarConstant(Op::OpTypeInt, opcode, typeId, value);1849if (existing)1850return existing;1851}18521853Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1854c->addImmediateOperand(value);1855constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1856module.mapInstruction(c);18571858Id resultId = c->getResultId();1859if (!specConstant) {1860ScalarConstantKey key{ enumCast(Op::OpTypeInt), enumCast(opcode), typeId, value, 0 };1861groupedScalarConstantResultIDs[key] = resultId;1862}1863return resultId;1864}18651866Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant)1867{1868Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant;18691870unsigned op1 = value & 0xFFFFFFFF;1871unsigned op2 = value >> 32;18721873// See if we already made it. Applies only to regular constants, because specialization constants1874// must remain distinct for the purpose of applying a SpecId decoration.1875if (! specConstant) {1876Id existing = findScalarConstant(Op::OpTypeInt, opcode, typeId, op1, op2);1877if (existing)1878return existing;1879}18801881Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1882c->reserveOperands(2);1883c->addImmediateOperand(op1);1884c->addImmediateOperand(op2);1885constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1886module.mapInstruction(c);18871888Id resultId = c->getResultId();1889if (!specConstant) {1890ScalarConstantKey key{ enumCast(Op::OpTypeInt), enumCast(opcode), typeId, op1, op2 };1891groupedScalarConstantResultIDs[key] = resultId;1892}1893return resultId;1894}18951896Id Builder::makeFloatConstant(float f, bool specConstant)1897{1898Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant;1899Id typeId = makeFloatType(32);1900union { float fl; unsigned int ui; } u;1901u.fl = f;1902unsigned value = u.ui;19031904// See if we already made it. Applies only to regular constants, because specialization constants1905// must remain distinct for the purpose of applying a SpecId decoration.1906if (! specConstant) {1907Id existing = findScalarConstant(Op::OpTypeFloat, opcode, typeId, value);1908if (existing)1909return existing;1910}19111912Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1913c->addImmediateOperand(value);1914constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1915module.mapInstruction(c);19161917Id resultId = c->getResultId();1918if (!specConstant) {1919ScalarConstantKey key{ enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0 };1920groupedScalarConstantResultIDs[key] = resultId;1921}1922return resultId;1923}19241925Id Builder::makeDoubleConstant(double d, bool specConstant)1926{1927Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant;1928Id typeId = makeFloatType(64);1929union { double db; unsigned long long ull; } u;1930u.db = d;1931unsigned long long value = u.ull;1932unsigned op1 = value & 0xFFFFFFFF;1933unsigned op2 = value >> 32;19341935// See if we already made it. Applies only to regular constants, because specialization constants1936// must remain distinct for the purpose of applying a SpecId decoration.1937if (! specConstant) {1938Id existing = findScalarConstant(Op::OpTypeFloat, opcode, typeId, op1, op2);1939if (existing)1940return existing;1941}19421943Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1944c->reserveOperands(2);1945c->addImmediateOperand(op1);1946c->addImmediateOperand(op2);1947constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1948module.mapInstruction(c);19491950Id resultId = c->getResultId();1951if (!specConstant) {1952ScalarConstantKey key{ enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, op1, op2 };1953groupedScalarConstantResultIDs[key] = resultId;1954}1955return resultId;1956}19571958Id Builder::makeFloat16Constant(float f16, bool specConstant)1959{1960Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant;1961Id typeId = makeFloatType(16);19621963spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16);1964spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0);1965fVal.castTo(f16Val, spvutils::kRoundToZero);19661967unsigned value = f16Val.value().getAsFloat().get_value();19681969// See if we already made it. Applies only to regular constants, because specialization constants1970// must remain distinct for the purpose of applying a SpecId decoration.1971if (!specConstant) {1972Id existing = findScalarConstant(Op::OpTypeFloat, opcode, typeId, value);1973if (existing)1974return existing;1975}19761977Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1978c->addImmediateOperand(value);1979constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1980module.mapInstruction(c);19811982Id resultId = c->getResultId();1983if (!specConstant) {1984ScalarConstantKey key{ enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0 };1985groupedScalarConstantResultIDs[key] = resultId;1986}1987return resultId;1988}19891990Id Builder::makeBFloat16Constant(float bf16, bool specConstant)1991{1992Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant;1993Id typeId = makeBFloat16Type();19941995union {1996float f;1997uint32_t u;1998} un;1999un.f = bf16;20002001// take high 16b of fp32 value. This is effectively round-to-zero, other than certain NaNs.2002unsigned value = un.u >> 16;20032004// See if we already made it. Applies only to regular constants, because specialization constants2005// must remain distinct for the purpose of applying a SpecId decoration.2006if (!specConstant) {2007Id existing = findScalarConstant(Op::OpTypeFloat, opcode, typeId, value);2008if (existing)2009return existing;2010}20112012Instruction* c = new Instruction(getUniqueId(), typeId, opcode);2013c->addImmediateOperand(value);2014constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));2015module.mapInstruction(c);20162017Id resultId = c->getResultId();2018if (!specConstant) {2019ScalarConstantKey key{ enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0 };2020groupedScalarConstantResultIDs[key] = resultId;2021}2022return resultId;2023}20242025Id Builder::makeFloatE5M2Constant(float fe5m2, bool specConstant)2026{2027Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant;2028Id typeId = makeFloatE5M2Type();20292030spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(fe5m2);2031spvutils::HexFloat<spvutils::FloatProxy<spvutils::FloatE5M2>> fe5m2Val(0);2032fVal.castTo(fe5m2Val, spvutils::kRoundToZero);20332034unsigned value = fe5m2Val.value().getAsFloat().get_value();20352036// See if we already made it. Applies only to regular constants, because specialization constants2037// must remain distinct for the purpose of applying a SpecId decoration.2038if (!specConstant) {2039Id existing = findScalarConstant(Op::OpTypeFloat, opcode, typeId, value);2040if (existing)2041return existing;2042}20432044Instruction* c = new Instruction(getUniqueId(), typeId, opcode);2045c->addImmediateOperand(value);2046constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));2047module.mapInstruction(c);20482049Id resultId = c->getResultId();2050if (!specConstant) {2051ScalarConstantKey key{enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0};2052groupedScalarConstantResultIDs[key] = resultId;2053}2054return resultId;2055}20562057Id Builder::makeFloatE4M3Constant(float fe4m3, bool specConstant)2058{2059Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant;2060Id typeId = makeFloatE4M3Type();20612062spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(fe4m3);2063spvutils::HexFloat<spvutils::FloatProxy<spvutils::FloatE4M3>> fe4m3Val(0);2064fVal.castTo(fe4m3Val, spvutils::kRoundToZero);20652066unsigned value = fe4m3Val.value().getAsFloat().get_value();20672068// See if we already made it. Applies only to regular constants, because specialization constants2069// must remain distinct for the purpose of applying a SpecId decoration.2070if (!specConstant) {2071Id existing = findScalarConstant(Op::OpTypeFloat, opcode, typeId, value);2072if (existing)2073return existing;2074}20752076Instruction* c = new Instruction(getUniqueId(), typeId, opcode);2077c->addImmediateOperand(value);2078constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));2079module.mapInstruction(c);20802081Id resultId = c->getResultId();2082if (!specConstant) {2083ScalarConstantKey key{enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0};2084groupedScalarConstantResultIDs[key] = resultId;2085}2086return resultId;2087}20882089Id Builder::makeFpConstant(Id type, double d, bool specConstant)2090{2091const int width = getScalarTypeWidth(type);20922093assert(isFloatType(type));20942095switch (width) {2096case 16:2097return makeFloat16Constant((float)d, specConstant);2098case 32:2099return makeFloatConstant((float)d, specConstant);2100case 64:2101return makeDoubleConstant(d, specConstant);2102default:2103break;2104}21052106assert(false);2107return NoResult;2108}21092110Id Builder::importNonSemanticShaderDebugInfoInstructions()2111{2112assert(emitNonSemanticShaderDebugInfo == true);21132114if(nonSemanticShaderDebugInfo == 0)2115{2116this->addExtension(spv::E_SPV_KHR_non_semantic_info);2117nonSemanticShaderDebugInfo = this->import("NonSemantic.Shader.DebugInfo.100");2118}21192120return nonSemanticShaderDebugInfo;2121}21222123Id Builder::findCompositeConstant(Op typeClass, Op opcode, Id typeId, const std::vector<Id>& comps, size_t numMembers)2124{2125Instruction* constant = nullptr;2126bool found = false;2127for (int i = 0; i < (int)groupedCompositeConstants[enumCast(typeClass)].size(); ++i) {2128constant = groupedCompositeConstants[enumCast(typeClass)][i];21292130if (constant->getTypeId() != typeId)2131continue;21322133if (constant->getOpCode() != opcode) {2134continue;2135}21362137if (constant->getNumOperands() != (int)numMembers)2138continue;21392140// same contents?2141bool mismatch = false;2142for (int op = 0; op < constant->getNumOperands(); ++op) {2143if (constant->getIdOperand(op) != comps[op]) {2144mismatch = true;2145break;2146}2147}2148if (! mismatch) {2149found = true;2150break;2151}2152}21532154return found ? constant->getResultId() : NoResult;2155}21562157Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps)2158{2159Instruction* constant = nullptr;2160bool found = false;2161for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) {2162constant = groupedStructConstants[typeId][i];21632164// same contents?2165bool mismatch = false;2166for (int op = 0; op < constant->getNumOperands(); ++op) {2167if (constant->getIdOperand(op) != comps[op]) {2168mismatch = true;2169break;2170}2171}2172if (! mismatch) {2173found = true;2174break;2175}2176}21772178return found ? constant->getResultId() : NoResult;2179}21802181// Comments in header2182Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant)2183{2184assert(typeId);2185Op typeClass = getTypeClass(typeId);21862187bool replicate = false;2188size_t numMembers = members.size();2189if (useReplicatedComposites || typeClass == Op::OpTypeCooperativeVectorNV) {2190// use replicate if all members are the same2191replicate = numMembers > 0 &&2192std::equal(members.begin() + 1, members.end(), members.begin());21932194if (replicate) {2195numMembers = 1;2196addCapability(spv::Capability::ReplicatedCompositesEXT);2197addExtension(spv::E_SPV_EXT_replicated_composites);2198}2199}22002201Op opcode = replicate ?2202(specConstant ? Op::OpSpecConstantCompositeReplicateEXT : Op::OpConstantCompositeReplicateEXT) :2203(specConstant ? Op::OpSpecConstantComposite : Op::OpConstantComposite);22042205switch (typeClass) {2206case Op::OpTypeVector:2207case Op::OpTypeArray:2208case Op::OpTypeMatrix:2209case Op::OpTypeCooperativeMatrixKHR:2210case Op::OpTypeCooperativeMatrixNV:2211case Op::OpTypeCooperativeVectorNV:2212if (! specConstant) {2213Id existing = findCompositeConstant(typeClass, opcode, typeId, members, numMembers);2214if (existing)2215return existing;2216}2217break;2218case Op::OpTypeStruct:2219if (! specConstant) {2220Id existing = findStructConstant(typeId, members);2221if (existing)2222return existing;2223}2224break;2225default:2226assert(0);2227return makeFloatConstant(0.0);2228}22292230Instruction* c = new Instruction(getUniqueId(), typeId, opcode);2231c->reserveOperands(members.size());2232for (size_t op = 0; op < numMembers; ++op)2233c->addIdOperand(members[op]);2234constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));2235if (typeClass == Op::OpTypeStruct)2236groupedStructConstants[typeId].push_back(c);2237else2238groupedCompositeConstants[enumCast(typeClass)].push_back(c);2239module.mapInstruction(c);22402241return c->getResultId();2242}22432244Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name)2245{2246Instruction* entryPoint = new Instruction(Op::OpEntryPoint);2247entryPoint->reserveOperands(3);2248entryPoint->addImmediateOperand(model);2249entryPoint->addIdOperand(function->getId());2250entryPoint->addStringOperand(name);22512252entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint));22532254return entryPoint;2255}22562257// Currently relying on the fact that all 'value' of interest are small non-negative values.2258void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3)2259{2260// entryPoint can be null if we are in compile-only mode2261if (!entryPoint)2262return;22632264Instruction* instr = new Instruction(Op::OpExecutionMode);2265instr->reserveOperands(3);2266instr->addIdOperand(entryPoint->getId());2267instr->addImmediateOperand(mode);2268if (value1 >= 0)2269instr->addImmediateOperand(value1);2270if (value2 >= 0)2271instr->addImmediateOperand(value2);2272if (value3 >= 0)2273instr->addImmediateOperand(value3);22742275executionModes.push_back(std::unique_ptr<Instruction>(instr));2276}22772278void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector<unsigned>& literals)2279{2280// entryPoint can be null if we are in compile-only mode2281if (!entryPoint)2282return;22832284Instruction* instr = new Instruction(Op::OpExecutionMode);2285instr->reserveOperands(literals.size() + 2);2286instr->addIdOperand(entryPoint->getId());2287instr->addImmediateOperand(mode);2288for (auto literal : literals)2289instr->addImmediateOperand(literal);22902291executionModes.push_back(std::unique_ptr<Instruction>(instr));2292}22932294void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector<Id>& operandIds)2295{2296// entryPoint can be null if we are in compile-only mode2297if (!entryPoint)2298return;22992300Instruction* instr = new Instruction(Op::OpExecutionModeId);2301instr->reserveOperands(operandIds.size() + 2);2302instr->addIdOperand(entryPoint->getId());2303instr->addImmediateOperand(mode);2304for (auto operandId : operandIds)2305instr->addIdOperand(operandId);23062307executionModes.push_back(std::unique_ptr<Instruction>(instr));2308}23092310void Builder::addName(Id id, const char* string)2311{2312Instruction* name = new Instruction(Op::OpName);2313name->reserveOperands(2);2314name->addIdOperand(id);2315name->addStringOperand(string);23162317names.push_back(std::unique_ptr<Instruction>(name));2318}23192320void Builder::addMemberName(Id id, int memberNumber, const char* string)2321{2322Instruction* name = new Instruction(Op::OpMemberName);2323name->reserveOperands(3);2324name->addIdOperand(id);2325name->addImmediateOperand(memberNumber);2326name->addStringOperand(string);23272328names.push_back(std::unique_ptr<Instruction>(name));2329}23302331void Builder::addDecoration(Id id, Decoration decoration, int num)2332{2333if (decoration == spv::Decoration::Max)2334return;23352336Instruction* dec = new Instruction(Op::OpDecorate);2337dec->reserveOperands(2);2338dec->addIdOperand(id);2339dec->addImmediateOperand(decoration);2340if (num >= 0)2341dec->addImmediateOperand(num);23422343decorations.insert(std::unique_ptr<Instruction>(dec));2344}23452346void Builder::addDecoration(Id id, Decoration decoration, const char* s)2347{2348if (decoration == spv::Decoration::Max)2349return;23502351Instruction* dec = new Instruction(Op::OpDecorateString);2352dec->reserveOperands(3);2353dec->addIdOperand(id);2354dec->addImmediateOperand(decoration);2355dec->addStringOperand(s);23562357decorations.insert(std::unique_ptr<Instruction>(dec));2358}23592360void Builder::addDecoration(Id id, Decoration decoration, const std::vector<unsigned>& literals)2361{2362if (decoration == spv::Decoration::Max)2363return;23642365Instruction* dec = new Instruction(Op::OpDecorate);2366dec->reserveOperands(literals.size() + 2);2367dec->addIdOperand(id);2368dec->addImmediateOperand(decoration);2369for (auto literal : literals)2370dec->addImmediateOperand(literal);23712372decorations.insert(std::unique_ptr<Instruction>(dec));2373}23742375void Builder::addDecoration(Id id, Decoration decoration, const std::vector<const char*>& strings)2376{2377if (decoration == spv::Decoration::Max)2378return;23792380Instruction* dec = new Instruction(Op::OpDecorateString);2381dec->reserveOperands(strings.size() + 2);2382dec->addIdOperand(id);2383dec->addImmediateOperand(decoration);2384for (auto string : strings)2385dec->addStringOperand(string);23862387decorations.insert(std::unique_ptr<Instruction>(dec));2388}23892390void Builder::addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType) {2391Instruction* dec = new Instruction(Op::OpDecorate);2392dec->reserveOperands(4);2393dec->addIdOperand(id);2394dec->addImmediateOperand(spv::Decoration::LinkageAttributes);2395dec->addStringOperand(name);2396dec->addImmediateOperand(linkType);23972398decorations.insert(std::unique_ptr<Instruction>(dec));2399}24002401void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration)2402{2403if (decoration == spv::Decoration::Max)2404return;24052406Instruction* dec = new Instruction(Op::OpDecorateId);2407dec->reserveOperands(3);2408dec->addIdOperand(id);2409dec->addImmediateOperand(decoration);2410dec->addIdOperand(idDecoration);24112412decorations.insert(std::unique_ptr<Instruction>(dec));2413}24142415void Builder::addDecorationId(Id id, Decoration decoration, const std::vector<Id>& operandIds)2416{2417if(decoration == spv::Decoration::Max)2418return;24192420Instruction* dec = new Instruction(Op::OpDecorateId);2421dec->reserveOperands(operandIds.size() + 2);2422dec->addIdOperand(id);2423dec->addImmediateOperand(decoration);24242425for (auto operandId : operandIds)2426dec->addIdOperand(operandId);24272428decorations.insert(std::unique_ptr<Instruction>(dec));2429}24302431void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num)2432{2433if (decoration == spv::Decoration::Max)2434return;24352436Instruction* dec = new Instruction(Op::OpMemberDecorate);2437dec->reserveOperands(3);2438dec->addIdOperand(id);2439dec->addImmediateOperand(member);2440dec->addImmediateOperand(decoration);2441if (num >= 0)2442dec->addImmediateOperand(num);24432444decorations.insert(std::unique_ptr<Instruction>(dec));2445}24462447void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s)2448{2449if (decoration == spv::Decoration::Max)2450return;24512452Instruction* dec = new Instruction(Op::OpMemberDecorateStringGOOGLE);2453dec->reserveOperands(4);2454dec->addIdOperand(id);2455dec->addImmediateOperand(member);2456dec->addImmediateOperand(decoration);2457dec->addStringOperand(s);24582459decorations.insert(std::unique_ptr<Instruction>(dec));2460}24612462void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<unsigned>& literals)2463{2464if (decoration == spv::Decoration::Max)2465return;24662467Instruction* dec = new Instruction(Op::OpMemberDecorate);2468dec->reserveOperands(literals.size() + 3);2469dec->addIdOperand(id);2470dec->addImmediateOperand(member);2471dec->addImmediateOperand(decoration);2472for (auto literal : literals)2473dec->addImmediateOperand(literal);24742475decorations.insert(std::unique_ptr<Instruction>(dec));2476}24772478void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<const char*>& strings)2479{2480if (decoration == spv::Decoration::Max)2481return;24822483Instruction* dec = new Instruction(Op::OpMemberDecorateString);2484dec->reserveOperands(strings.size() + 3);2485dec->addIdOperand(id);2486dec->addImmediateOperand(member);2487dec->addImmediateOperand(decoration);2488for (auto string : strings)2489dec->addStringOperand(string);24902491decorations.insert(std::unique_ptr<Instruction>(dec));2492}24932494void Builder::addInstruction(std::unique_ptr<Instruction> inst) {2495// Phis must appear first in their block, don't insert line tracking instructions2496// in front of them, just add the OpPhi and return.2497if (inst->getOpCode() == Op::OpPhi) {2498buildPoint->addInstruction(std::move(inst));2499return;2500}2501// Optionally insert OpDebugScope2502if (emitNonSemanticShaderDebugInfo && dirtyScopeTracker) {2503if (buildPoint->updateDebugScope(currentDebugScopeId.top())) {2504auto scopeInst = std::make_unique<Instruction>(getUniqueId(), makeVoidType(), Op::OpExtInst);2505scopeInst->reserveOperands(3);2506scopeInst->addIdOperand(nonSemanticShaderDebugInfo);2507scopeInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugScope);2508scopeInst->addIdOperand(currentDebugScopeId.top());2509buildPoint->addInstruction(std::move(scopeInst));2510}25112512dirtyScopeTracker = false;2513}25142515// Insert OpLine/OpDebugLine if the debug source location has changed2516if (trackDebugInfo && dirtyLineTracker) {2517if (buildPoint->updateDebugSourceLocation(currentLine, 0, currentFileId)) {2518if (emitSpirvDebugInfo) {2519auto lineInst = std::make_unique<Instruction>(Op::OpLine);2520lineInst->reserveOperands(3);2521lineInst->addIdOperand(currentFileId);2522lineInst->addImmediateOperand(currentLine);2523lineInst->addImmediateOperand(0);2524buildPoint->addInstruction(std::move(lineInst));2525}2526if (emitNonSemanticShaderDebugInfo) {2527auto lineInst = std::make_unique<Instruction>(getUniqueId(), makeVoidType(), Op::OpExtInst);2528lineInst->reserveOperands(7);2529lineInst->addIdOperand(nonSemanticShaderDebugInfo);2530lineInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLine);2531lineInst->addIdOperand(makeDebugSource(currentFileId));2532lineInst->addIdOperand(makeUintConstant(currentLine));2533lineInst->addIdOperand(makeUintConstant(currentLine));2534lineInst->addIdOperand(makeUintConstant(0));2535lineInst->addIdOperand(makeUintConstant(0));2536buildPoint->addInstruction(std::move(lineInst));2537}2538}25392540dirtyLineTracker = false;2541}25422543buildPoint->addInstruction(std::move(inst));2544}25452546void Builder::addInstructionNoDebugInfo(std::unique_ptr<Instruction> inst) {2547buildPoint->addInstruction(std::move(inst));2548}25492550// Comments in header2551Function* Builder::makeEntryPoint(const char* entryPoint)2552{2553assert(! entryPointFunction);25542555auto const returnType = makeVoidType();25562557restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo;2558if(sourceLang == spv::SourceLanguage::HLSL) {2559emitNonSemanticShaderDebugInfo = false;2560}25612562Block* entry = nullptr;2563entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, LinkageType::Max, {}, {}, &entry);25642565emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo;25662567return entryPointFunction;2568}25692570// Comments in header2571Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType,2572const std::vector<Id>& paramTypes,2573const std::vector<std::vector<Decoration>>& decorations, Block** entry)2574{2575// Make the function and initial instructions in it2576Id typeId = makeFunctionType(returnType, paramTypes);2577Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size());2578Id funcId = getUniqueId();2579Function* function = new Function(funcId, returnType, typeId, firstParamId, linkType, name, module);25802581// Set up the precisions2582setPrecision(function->getId(), precision);2583function->setReturnPrecision(precision);2584for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) {2585for (int d = 0; d < (int)decorations[p].size(); ++d) {2586addDecoration(firstParamId + p, decorations[p][d]);2587function->addParamPrecision(p, decorations[p][d]);2588}2589}25902591// reset last debug scope2592if (emitNonSemanticShaderDebugInfo) {2593dirtyScopeTracker = true;2594}25952596// CFG2597assert(entry != nullptr);2598*entry = new Block(getUniqueId(), *function);2599function->addBlock(*entry);2600setBuildPoint(*entry);26012602if (name)2603addName(function->getId(), name);26042605functions.push_back(std::unique_ptr<Function>(function));26062607return function;2608}26092610void Builder::setupFunctionDebugInfo(Function* function, const char* name, const std::vector<Id>& paramTypes,2611const std::vector<char const*>& paramNames)2612{26132614if (!emitNonSemanticShaderDebugInfo)2615return;26162617Id nameId = getStringId(unmangleFunctionName(name));2618Id funcTypeId = function->getFuncTypeId();2619assert(getDebugType(funcTypeId) != NoType);2620Id funcId = function->getId();26212622assert(funcId != 0);26232624// Make the debug function instruction2625Id debugFuncId = makeDebugFunction(function, nameId, funcTypeId);2626debugFuncIdLookup[funcId] = debugFuncId;2627currentDebugScopeId.push(debugFuncId);26282629// DebugScope and DebugLine for parameter DebugDeclares2630assert(paramTypes.size() == paramNames.size());2631if ((int)paramTypes.size() > 0) {2632Id firstParamId = function->getParamId(0);26332634for (size_t p = 0; p < paramTypes.size(); ++p) {2635bool passByRef = false;2636Id paramTypeId = paramTypes[p];26372638// For pointer-typed parameters, they are actually passed by reference and we need unwrap the pointer to get the actual parameter type.2639if (isPointerType(paramTypeId) || isArrayType(paramTypeId)) {2640passByRef = true;2641paramTypeId = getContainedTypeId(paramTypeId);2642}26432644auto const& paramName = paramNames[p];2645auto const debugLocalVariableId = createDebugLocalVariable(getDebugType(paramTypeId), paramName, p + 1);2646auto const paramId = static_cast<Id>(firstParamId + p);26472648if (passByRef) {2649makeDebugDeclare(debugLocalVariableId, paramId);2650} else {2651makeDebugValue(debugLocalVariableId, paramId);2652}2653}2654}26552656// Clear debug scope stack2657if (emitNonSemanticShaderDebugInfo)2658currentDebugScopeId.pop();2659}26602661Id Builder::makeDebugFunction([[maybe_unused]] Function* function, Id nameId, Id funcTypeId)2662{2663assert(function != nullptr);2664assert(nameId != 0);2665assert(funcTypeId != 0);2666assert(getDebugType(funcTypeId) != NoType);26672668Id funcId = getUniqueId();2669auto type = new Instruction(funcId, makeVoidType(), Op::OpExtInst);2670type->reserveOperands(11);2671type->addIdOperand(nonSemanticShaderDebugInfo);2672type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugFunction);2673type->addIdOperand(nameId);2674type->addIdOperand(getDebugType(funcTypeId));2675type->addIdOperand(makeDebugSource(currentFileId)); // TODO: This points to file of definition instead of declaration2676type->addIdOperand(makeUintConstant(currentLine)); // TODO: This points to line of definition instead of declaration2677type->addIdOperand(makeUintConstant(0)); // column2678type->addIdOperand(makeDebugCompilationUnit()); // scope2679type->addIdOperand(nameId); // linkage name2680type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic));2681type->addIdOperand(makeUintConstant(currentLine));2682constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));2683module.mapInstruction(type);2684return funcId;2685}26862687Id Builder::makeDebugLexicalBlock(uint32_t line, uint32_t column) {2688assert(!currentDebugScopeId.empty());26892690Id lexId = getUniqueId();2691auto lex = new Instruction(lexId, makeVoidType(), Op::OpExtInst);2692lex->reserveOperands(6);2693lex->addIdOperand(nonSemanticShaderDebugInfo);2694lex->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLexicalBlock);2695lex->addIdOperand(makeDebugSource(currentFileId));2696lex->addIdOperand(makeUintConstant(line));2697lex->addIdOperand(makeUintConstant(column)); // column2698lex->addIdOperand(currentDebugScopeId.top()); // scope2699constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(lex));2700module.mapInstruction(lex);2701return lexId;2702}27032704std::string Builder::unmangleFunctionName(std::string const& name) const2705{2706assert(name.length() > 0);27072708if(name.rfind('(') != std::string::npos) {2709return name.substr(0, name.rfind('('));2710} else {2711return name;2712}2713}27142715// Comments in header2716void Builder::makeReturn(bool implicit, Id retVal)2717{2718if (retVal) {2719Instruction* inst = new Instruction(NoResult, NoType, Op::OpReturnValue);2720inst->addIdOperand(retVal);2721addInstruction(std::unique_ptr<Instruction>(inst));2722} else2723addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, Op::OpReturn)));27242725if (! implicit)2726createAndSetNoPredecessorBlock("post-return");2727}27282729// Comments in header2730void Builder::enterLexicalBlock(uint32_t line, uint32_t column)2731{2732if (!emitNonSemanticShaderDebugInfo) {2733return;2734}27352736// Generate new lexical scope debug instruction2737Id lexId = makeDebugLexicalBlock(line, column);2738currentDebugScopeId.push(lexId);2739dirtyScopeTracker = true;2740}27412742// Comments in header2743void Builder::leaveLexicalBlock()2744{2745if (!emitNonSemanticShaderDebugInfo) {2746return;2747}27482749// Pop current scope from stack and clear current scope2750currentDebugScopeId.pop();2751dirtyScopeTracker = true;2752}27532754// Comments in header2755void Builder::enterFunction(Function const* function)2756{2757currentFunction = function;27582759// Save and disable debugInfo for HLSL entry point function. It is a wrapper2760// function with no user code in it.2761restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo;2762if (sourceLang == spv::SourceLanguage::HLSL && function == entryPointFunction) {2763emitNonSemanticShaderDebugInfo = false;2764}27652766if (emitNonSemanticShaderDebugInfo) {2767// Initialize scope state2768Id funcId = function->getFuncId();2769Id debugFuncId = getDebugFunction(funcId);2770currentDebugScopeId.push(debugFuncId);2771// Create DebugFunctionDefinition2772spv::Id resultId = getUniqueId();2773Instruction* defInst = new Instruction(resultId, makeVoidType(), Op::OpExtInst);2774defInst->reserveOperands(4);2775defInst->addIdOperand(nonSemanticShaderDebugInfo);2776defInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugFunctionDefinition);2777defInst->addIdOperand(debugFuncId);2778defInst->addIdOperand(funcId);2779addInstruction(std::unique_ptr<Instruction>(defInst));2780}27812782if (auto linkType = function->getLinkType(); linkType != LinkageType::Max) {2783Id funcId = function->getFuncId();2784addCapability(Capability::Linkage);2785addLinkageDecoration(funcId, function->getExportName(), linkType);2786}2787}27882789// Comments in header2790void Builder::leaveFunction()2791{2792Block* block = buildPoint;2793Function& function = buildPoint->getParent();2794assert(block);27952796// If our function did not contain a return, add a return void now.2797if (! block->isTerminated()) {2798if (function.getReturnType() == makeVoidType())2799makeReturn(true);2800else {2801makeReturn(true, createUndefined(function.getReturnType()));2802}2803}28042805// Clear function scope from debug scope stack2806if (emitNonSemanticShaderDebugInfo)2807currentDebugScopeId.pop();28082809emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo;28102811// Clear current function record2812currentFunction = nullptr;2813}28142815// Comments in header2816void Builder::makeStatementTerminator(spv::Op opcode, const char *name)2817{2818addInstruction(std::unique_ptr<Instruction>(new Instruction(opcode)));2819createAndSetNoPredecessorBlock(name);2820}28212822// Comments in header2823void Builder::makeStatementTerminator(spv::Op opcode, const std::vector<Id>& operands, const char* name)2824{2825// It's assumed that the terminator instruction is always of void return type2826// However in future if there is a need for non void return type, new helper2827// methods can be created.2828createNoResultOp(opcode, operands);2829createAndSetNoPredecessorBlock(name);2830}28312832void Builder::createConstVariable(Id type, const char* name, Id constant, bool isGlobal)2833{2834if (emitNonSemanticShaderDebugInfo) {2835Id debugType = getDebugType(type);2836if (isGlobal) {2837createDebugGlobalVariable(debugType, name, constant);2838}2839else {2840auto debugLocal = createDebugLocalVariable(debugType, name);2841makeDebugValue(debugLocal, constant);2842}2843}2844}28452846// Comments in header2847Id Builder::createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name, Id initializer,2848bool const compilerGenerated)2849{2850Id pointerType = makePointer(storageClass, type);2851Instruction* inst = new Instruction(getUniqueId(), pointerType, Op::OpVariable);2852inst->addImmediateOperand(storageClass);2853if (initializer != NoResult)2854inst->addIdOperand(initializer);28552856if (storageClass == StorageClass::Function) {2857// Validation rules require the declaration in the entry block2858buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst));2859}2860else {2861constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));2862module.mapInstruction(inst);2863}28642865if (emitNonSemanticShaderDebugInfo && !compilerGenerated)2866{2867// For debug info, we prefer respecting how the variable is declared in source code.2868// We may emulate some local variables as global variable with private storage in SPIR-V, but we still want to2869// treat them as local variables in debug info.2870if (storageClass == StorageClass::Function || (currentFunction && storageClass == StorageClass::Private)) {2871auto const debugLocalVariableId = createDebugLocalVariable(getDebugType(type), name);2872makeDebugDeclare(debugLocalVariableId, inst->getResultId());2873}2874else {2875createDebugGlobalVariable(getDebugType(type), name, inst->getResultId());2876}2877}28782879if (name)2880addName(inst->getResultId(), name);2881setPrecision(inst->getResultId(), precision);28822883return inst->getResultId();2884}28852886// Comments in header2887Id Builder::createUndefined(Id type)2888{2889Instruction* inst = new Instruction(getUniqueId(), type, Op::OpUndef);2890addInstruction(std::unique_ptr<Instruction>(inst));2891return inst->getResultId();2892}28932894// av/vis/nonprivate are unnecessary and illegal for some storage classes.2895spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)2896const2897{2898switch (sc) {2899case spv::StorageClass::Uniform:2900case spv::StorageClass::Workgroup:2901case spv::StorageClass::StorageBuffer:2902case spv::StorageClass::PhysicalStorageBufferEXT:2903break;2904default:2905memoryAccess = spv::MemoryAccessMask(memoryAccess &2906~(spv::MemoryAccessMask::MakePointerAvailableKHR |2907spv::MemoryAccessMask::MakePointerVisibleKHR |2908spv::MemoryAccessMask::NonPrivatePointerKHR));2909break;2910}2911return memoryAccess;2912}29132914// Comments in header2915void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope,2916unsigned int alignment)2917{2918Instruction* store = new Instruction(Op::OpStore);2919store->reserveOperands(2);2920store->addIdOperand(lValue);2921store->addIdOperand(rValue);29222923memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));29242925if (memoryAccess != MemoryAccessMask::MaskNone) {2926store->addImmediateOperand(memoryAccess);2927if (anySet(memoryAccess, spv::MemoryAccessMask::Aligned)) {2928store->addImmediateOperand(alignment);2929}2930if (anySet(memoryAccess, spv::MemoryAccessMask::MakePointerAvailableKHR)) {2931store->addIdOperand(makeUintConstant(scope));2932}2933}29342935addInstruction(std::unique_ptr<Instruction>(store));2936}29372938// Comments in header2939Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMask memoryAccess,2940spv::Scope scope, unsigned int alignment)2941{2942Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), Op::OpLoad);2943load->addIdOperand(lValue);29442945memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));29462947if (memoryAccess != MemoryAccessMask::MaskNone) {2948load->addImmediateOperand(memoryAccess);2949if (anySet(memoryAccess, spv::MemoryAccessMask::Aligned)) {2950load->addImmediateOperand(alignment);2951}2952if (anySet(memoryAccess, spv::MemoryAccessMask::MakePointerVisibleKHR)) {2953load->addIdOperand(makeUintConstant(scope));2954}2955}29562957addInstruction(std::unique_ptr<Instruction>(load));2958setPrecision(load->getResultId(), precision);29592960return load->getResultId();2961}29622963// Comments in header2964Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets)2965{2966// Figure out the final resulting type.2967Id typeId = getResultingAccessChainType();2968typeId = makePointer(storageClass, typeId);29692970// Make the instruction2971Instruction* chain = new Instruction(getUniqueId(), typeId, Op::OpAccessChain);2972chain->reserveOperands(offsets.size() + 1);2973chain->addIdOperand(base);2974for (int i = 0; i < (int)offsets.size(); ++i)2975chain->addIdOperand(offsets[i]);2976addInstruction(std::unique_ptr<Instruction>(chain));29772978return chain->getResultId();2979}29802981Id Builder::createArrayLength(Id base, unsigned int member, unsigned int bits)2982{2983spv::Id intType = makeUintType(bits);2984Instruction* length = new Instruction(getUniqueId(), intType, Op::OpArrayLength);2985length->reserveOperands(2);2986length->addIdOperand(base);2987length->addImmediateOperand(member);2988addInstruction(std::unique_ptr<Instruction>(length));29892990return length->getResultId();2991}29922993Id Builder::createCooperativeMatrixLengthKHR(Id type)2994{2995spv::Id intType = makeUintType(32);29962997// Generate code for spec constants if in spec constant operation2998// generation mode.2999if (generatingOpCodeForSpecConst) {3000return createSpecConstantOp(Op::OpCooperativeMatrixLengthKHR, intType, std::vector<Id>(1, type), std::vector<Id>());3001}30023003Instruction* length = new Instruction(getUniqueId(), intType, Op::OpCooperativeMatrixLengthKHR);3004length->addIdOperand(type);3005addInstruction(std::unique_ptr<Instruction>(length));30063007return length->getResultId();3008}30093010Id Builder::createCooperativeMatrixLengthNV(Id type)3011{3012spv::Id intType = makeUintType(32);30133014// Generate code for spec constants if in spec constant operation3015// generation mode.3016if (generatingOpCodeForSpecConst) {3017return createSpecConstantOp(Op::OpCooperativeMatrixLengthNV, intType, std::vector<Id>(1, type), std::vector<Id>());3018}30193020Instruction* length = new Instruction(getUniqueId(), intType, Op::OpCooperativeMatrixLengthNV);3021length->addIdOperand(type);3022addInstruction(std::unique_ptr<Instruction>(length));30233024return length->getResultId();3025}30263027Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index)3028{3029// Generate code for spec constants if in spec constant operation3030// generation mode.3031if (generatingOpCodeForSpecConst) {3032return createSpecConstantOp(Op::OpCompositeExtract, typeId, std::vector<Id>(1, composite),3033std::vector<Id>(1, index));3034}3035Instruction* extract = new Instruction(getUniqueId(), typeId, Op::OpCompositeExtract);3036extract->reserveOperands(2);3037extract->addIdOperand(composite);3038extract->addImmediateOperand(index);3039addInstruction(std::unique_ptr<Instruction>(extract));30403041return extract->getResultId();3042}30433044Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes)3045{3046// Generate code for spec constants if in spec constant operation3047// generation mode.3048if (generatingOpCodeForSpecConst) {3049return createSpecConstantOp(Op::OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes);3050}3051Instruction* extract = new Instruction(getUniqueId(), typeId, Op::OpCompositeExtract);3052extract->reserveOperands(indexes.size() + 1);3053extract->addIdOperand(composite);3054for (int i = 0; i < (int)indexes.size(); ++i)3055extract->addImmediateOperand(indexes[i]);3056addInstruction(std::unique_ptr<Instruction>(extract));30573058return extract->getResultId();3059}30603061Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index)3062{3063Instruction* insert = new Instruction(getUniqueId(), typeId, Op::OpCompositeInsert);3064insert->reserveOperands(3);3065insert->addIdOperand(object);3066insert->addIdOperand(composite);3067insert->addImmediateOperand(index);3068addInstruction(std::unique_ptr<Instruction>(insert));30693070return insert->getResultId();3071}30723073Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes)3074{3075Instruction* insert = new Instruction(getUniqueId(), typeId, Op::OpCompositeInsert);3076insert->reserveOperands(indexes.size() + 2);3077insert->addIdOperand(object);3078insert->addIdOperand(composite);3079for (int i = 0; i < (int)indexes.size(); ++i)3080insert->addImmediateOperand(indexes[i]);3081addInstruction(std::unique_ptr<Instruction>(insert));30823083return insert->getResultId();3084}30853086Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex)3087{3088Instruction* extract = new Instruction(getUniqueId(), typeId, Op::OpVectorExtractDynamic);3089extract->reserveOperands(2);3090extract->addIdOperand(vector);3091extract->addIdOperand(componentIndex);3092addInstruction(std::unique_ptr<Instruction>(extract));30933094return extract->getResultId();3095}30963097Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex)3098{3099Instruction* insert = new Instruction(getUniqueId(), typeId, Op::OpVectorInsertDynamic);3100insert->reserveOperands(3);3101insert->addIdOperand(vector);3102insert->addIdOperand(component);3103insert->addIdOperand(componentIndex);3104addInstruction(std::unique_ptr<Instruction>(insert));31053106return insert->getResultId();3107}31083109// An opcode that has no operands, no result id, and no type3110void Builder::createNoResultOp(Op opCode)3111{3112Instruction* op = new Instruction(opCode);3113addInstruction(std::unique_ptr<Instruction>(op));3114}31153116// An opcode that has one id operand, no result id, and no type3117void Builder::createNoResultOp(Op opCode, Id operand)3118{3119Instruction* op = new Instruction(opCode);3120op->addIdOperand(operand);3121addInstruction(std::unique_ptr<Instruction>(op));3122}31233124// An opcode that has one or more operands, no result id, and no type3125void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)3126{3127Instruction* op = new Instruction(opCode);3128op->reserveOperands(operands.size());3129for (auto id : operands) {3130op->addIdOperand(id);3131}3132addInstruction(std::unique_ptr<Instruction>(op));3133}31343135// An opcode that has multiple operands, no result id, and no type3136void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands)3137{3138Instruction* op = new Instruction(opCode);3139op->reserveOperands(operands.size());3140for (auto it = operands.cbegin(); it != operands.cend(); ++it) {3141if (it->isId)3142op->addIdOperand(it->word);3143else3144op->addImmediateOperand(it->word);3145}3146addInstruction(std::unique_ptr<Instruction>(op));3147}31483149void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)3150{3151Instruction* op = new Instruction(Op::OpControlBarrier);3152op->reserveOperands(3);3153op->addIdOperand(makeUintConstant(execution));3154op->addIdOperand(makeUintConstant(memory));3155op->addIdOperand(makeUintConstant(semantics));3156addInstruction(std::unique_ptr<Instruction>(op));3157}31583159void Builder::createMemoryBarrier(Scope executionScope, MemorySemanticsMask memorySemantics)3160{3161Instruction* op = new Instruction(Op::OpMemoryBarrier);3162op->reserveOperands(2);3163op->addIdOperand(makeUintConstant((unsigned)executionScope));3164op->addIdOperand(makeUintConstant((unsigned)memorySemantics));3165addInstruction(std::unique_ptr<Instruction>(op));3166}31673168// An opcode that has one operands, a result id, and a type3169Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand)3170{3171// Generate code for spec constants if in spec constant operation3172// generation mode.3173if (generatingOpCodeForSpecConst) {3174return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>());3175}3176Instruction* op = new Instruction(getUniqueId(), typeId, opCode);3177op->addIdOperand(operand);3178addInstruction(std::unique_ptr<Instruction>(op));31793180return op->getResultId();3181}31823183Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right)3184{3185// Generate code for spec constants if in spec constant operation3186// generation mode.3187if (generatingOpCodeForSpecConst) {3188std::vector<Id> operands(2);3189operands[0] = left; operands[1] = right;3190return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>());3191}3192Instruction* op = new Instruction(getUniqueId(), typeId, opCode);3193op->reserveOperands(2);3194op->addIdOperand(left);3195op->addIdOperand(right);3196addInstruction(std::unique_ptr<Instruction>(op));31973198return op->getResultId();3199}32003201Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3)3202{3203// Generate code for spec constants if in spec constant operation3204// generation mode.3205if (generatingOpCodeForSpecConst) {3206std::vector<Id> operands(3);3207operands[0] = op1;3208operands[1] = op2;3209operands[2] = op3;3210return createSpecConstantOp(3211opCode, typeId, operands, std::vector<Id>());3212}3213Instruction* op = new Instruction(getUniqueId(), typeId, opCode);3214op->reserveOperands(3);3215op->addIdOperand(op1);3216op->addIdOperand(op2);3217op->addIdOperand(op3);3218addInstruction(std::unique_ptr<Instruction>(op));32193220return op->getResultId();3221}32223223Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands)3224{3225Instruction* op = new Instruction(getUniqueId(), typeId, opCode);3226op->reserveOperands(operands.size());3227for (auto id : operands)3228op->addIdOperand(id);3229addInstruction(std::unique_ptr<Instruction>(op));32303231return op->getResultId();3232}32333234Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands)3235{3236Instruction* op = new Instruction(getUniqueId(), typeId, opCode);3237op->reserveOperands(operands.size());3238for (auto it = operands.cbegin(); it != operands.cend(); ++it) {3239if (it->isId)3240op->addIdOperand(it->word);3241else3242op->addImmediateOperand(it->word);3243}3244addInstruction(std::unique_ptr<Instruction>(op));32453246return op->getResultId();3247}32483249Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands,3250const std::vector<unsigned>& literals)3251{3252Instruction* op = new Instruction(getUniqueId(), typeId, Op::OpSpecConstantOp);3253op->reserveOperands(operands.size() + literals.size() + 1);3254op->addImmediateOperand((unsigned) opCode);3255for (auto it = operands.cbegin(); it != operands.cend(); ++it)3256op->addIdOperand(*it);3257for (auto it = literals.cbegin(); it != literals.cend(); ++it)3258op->addImmediateOperand(*it);3259module.mapInstruction(op);3260constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op));32613262// OpSpecConstantOp's using 8 or 16 bit types require the associated capability3263if (containsType(typeId, Op::OpTypeInt, 8))3264addCapability(Capability::Int8);3265if (containsType(typeId, Op::OpTypeInt, 16))3266addCapability(Capability::Int16);3267if (containsType(typeId, Op::OpTypeFloat, 16))3268addCapability(Capability::Float16);32693270return op->getResultId();3271}32723273Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args)3274{3275Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), Op::OpFunctionCall);3276op->reserveOperands(args.size() + 1);3277op->addIdOperand(function->getId());3278for (int a = 0; a < (int)args.size(); ++a)3279op->addIdOperand(args[a]);3280addInstruction(std::unique_ptr<Instruction>(op));32813282return op->getResultId();3283}32843285// Comments in header3286Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels)3287{3288if (channels.size() == 1)3289return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision);32903291if (generatingOpCodeForSpecConst) {3292std::vector<Id> operands(2);3293operands[0] = operands[1] = source;3294return setPrecision(createSpecConstantOp(Op::OpVectorShuffle, typeId, operands, channels), precision);3295}3296Instruction* swizzle = new Instruction(getUniqueId(), typeId, Op::OpVectorShuffle);3297assert(isVector(source));3298swizzle->reserveOperands(channels.size() + 2);3299swizzle->addIdOperand(source);3300swizzle->addIdOperand(source);3301for (int i = 0; i < (int)channels.size(); ++i)3302swizzle->addImmediateOperand(channels[i]);3303addInstruction(std::unique_ptr<Instruction>(swizzle));33043305return setPrecision(swizzle->getResultId(), precision);3306}33073308// Comments in header3309Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels)3310{3311if (channels.size() == 1 && getNumComponents(source) == 1)3312return createCompositeInsert(source, target, typeId, channels.front());33133314Instruction* swizzle = new Instruction(getUniqueId(), typeId, Op::OpVectorShuffle);33153316assert(isVector(target));3317swizzle->reserveOperands(2);3318swizzle->addIdOperand(target);33193320assert(getNumComponents(source) == channels.size());3321assert(isVector(source));3322swizzle->addIdOperand(source);33233324// Set up an identity shuffle from the base value to the result value3325unsigned int components[4];3326int numTargetComponents = getNumComponents(target);3327for (int i = 0; i < numTargetComponents; ++i)3328components[i] = i;33293330// Punch in the l-value swizzle3331for (int i = 0; i < (int)channels.size(); ++i)3332components[channels[i]] = numTargetComponents + i;33333334// finish the instruction with these components selectors3335swizzle->reserveOperands(numTargetComponents);3336for (int i = 0; i < numTargetComponents; ++i)3337swizzle->addImmediateOperand(components[i]);3338addInstruction(std::unique_ptr<Instruction>(swizzle));33393340return swizzle->getResultId();3341}33423343// Comments in header3344void Builder::promoteScalar(Decoration precision, Id& left, Id& right)3345{3346int direction = getNumComponents(right) - getNumComponents(left);33473348if (direction > 0)3349left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right)));3350else if (direction < 0)3351right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left)));33523353return;3354}33553356// Comments in header3357Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType)3358{3359assert(getNumComponents(scalar) == 1);3360assert(getTypeId(scalar) == getScalarTypeId(vectorType));33613362int numComponents = getNumTypeComponents(vectorType);3363if (numComponents == 1 && !isCooperativeVectorType(vectorType))3364return scalar;33653366Instruction* smear = nullptr;3367if (generatingOpCodeForSpecConst) {3368auto members = std::vector<spv::Id>(numComponents, scalar);3369// Sometime even in spec-constant-op mode, the temporary vector created by3370// promoting a scalar might not be a spec constant. This should depend on3371// the scalar.3372// e.g.:3373// const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar;3374// In such cases, the temporary vector created from a_front_end_const_scalar3375// is not a spec constant vector, even though the binary operation node is marked3376// as 'specConstant' and we are in spec-constant-op mode.3377auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar));3378smear = module.getInstruction(result_id);3379} else {3380bool replicate = (useReplicatedComposites || isCooperativeVectorType(vectorType)) && (numComponents > 0);33813382if (replicate) {3383numComponents = 1;3384addCapability(spv::Capability::ReplicatedCompositesEXT);3385addExtension(spv::E_SPV_EXT_replicated_composites);3386}33873388Op opcode = replicate ? Op::OpCompositeConstructReplicateEXT : Op::OpCompositeConstruct;33893390smear = new Instruction(getUniqueId(), vectorType, opcode);3391smear->reserveOperands(numComponents);3392for (int c = 0; c < numComponents; ++c)3393smear->addIdOperand(scalar);3394addInstruction(std::unique_ptr<Instruction>(smear));3395}33963397return setPrecision(smear->getResultId(), precision);3398}33993400// Comments in header3401Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args)3402{3403Instruction* inst = new Instruction(getUniqueId(), resultType, Op::OpExtInst);3404inst->reserveOperands(args.size() + 2);3405inst->addIdOperand(builtins);3406inst->addImmediateOperand(entryPoint);3407for (int arg = 0; arg < (int)args.size(); ++arg)3408inst->addIdOperand(args[arg]);34093410addInstruction(std::unique_ptr<Instruction>(inst));34113412return inst->getResultId();3413}34143415// Accept all parameters needed to create a texture instruction.3416// Create the correct instruction based on the inputs, and make the call.3417Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,3418bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask)3419{3420std::vector<Id> texArgs;34213422//3423// Set up the fixed arguments3424//3425bool explicitLod = false;3426texArgs.push_back(parameters.sampler);3427texArgs.push_back(parameters.coords);3428if (parameters.Dref != NoResult)3429texArgs.push_back(parameters.Dref);3430if (parameters.component != NoResult)3431texArgs.push_back(parameters.component);34323433if (parameters.granularity != NoResult)3434texArgs.push_back(parameters.granularity);3435if (parameters.coarse != NoResult)3436texArgs.push_back(parameters.coarse);34373438//3439// Set up the optional arguments3440//3441size_t optArgNum = texArgs.size(); // the position of the mask for the optional arguments, if any.3442ImageOperandsMask mask = ImageOperandsMask::MaskNone; // the mask operand3443if (parameters.bias) {3444mask = (ImageOperandsMask)(mask | ImageOperandsMask::Bias);3445texArgs.push_back(parameters.bias);3446}3447if (parameters.lod) {3448mask = (ImageOperandsMask)(mask | ImageOperandsMask::Lod);3449texArgs.push_back(parameters.lod);3450explicitLod = true;3451} else if (parameters.gradX) {3452mask = (ImageOperandsMask)(mask | ImageOperandsMask::Grad);3453texArgs.push_back(parameters.gradX);3454texArgs.push_back(parameters.gradY);3455explicitLod = true;3456} else if (noImplicitLod && ! fetch && ! gather) {3457// have to explicitly use lod of 0 if not allowed to have them be implicit, and3458// we would otherwise be about to issue an implicit instruction3459mask = (ImageOperandsMask)(mask | ImageOperandsMask::Lod);3460texArgs.push_back(makeFloatConstant(0.0));3461explicitLod = true;3462}3463if (parameters.offset) {3464if (isConstant(parameters.offset))3465mask = (ImageOperandsMask)(mask | ImageOperandsMask::ConstOffset);3466else {3467addCapability(Capability::ImageGatherExtended);3468mask = (ImageOperandsMask)(mask | ImageOperandsMask::Offset);3469}3470texArgs.push_back(parameters.offset);3471}3472if (parameters.offsets) {3473if (!isConstant(parameters.offsets) && sourceLang == spv::SourceLanguage::GLSL) {3474mask = (ImageOperandsMask)(mask | ImageOperandsMask::Offsets);3475} else {3476addCapability(Capability::ImageGatherExtended);3477mask = (ImageOperandsMask)(mask | ImageOperandsMask::ConstOffsets);3478}3479texArgs.push_back(parameters.offsets);3480}3481if (parameters.sample) {3482mask = (ImageOperandsMask)(mask | ImageOperandsMask::Sample);3483texArgs.push_back(parameters.sample);3484}3485if (parameters.lodClamp) {3486// capability if this bit is used3487addCapability(Capability::MinLod);34883489mask = (ImageOperandsMask)(mask | ImageOperandsMask::MinLod);3490texArgs.push_back(parameters.lodClamp);3491}3492if (parameters.nonprivate) {3493mask = mask | ImageOperandsMask::NonPrivateTexelKHR;3494}3495if (parameters.volatil) {3496mask = mask | ImageOperandsMask::VolatileTexelKHR;3497}3498if (parameters.nontemporal) {3499mask = mask | ImageOperandsMask::Nontemporal;3500}3501mask = mask | signExtensionMask;3502// insert the operand for the mask, if any bits were set.3503if (mask != ImageOperandsMask::MaskNone)3504texArgs.insert(texArgs.begin() + optArgNum, (Id)mask);35053506//3507// Set up the instruction3508//3509Op opCode = Op::OpNop; // All paths below need to set this3510if (fetch) {3511if (sparse)3512opCode = Op::OpImageSparseFetch;3513else3514opCode = Op::OpImageFetch;3515} else if (parameters.granularity && parameters.coarse) {3516opCode = Op::OpImageSampleFootprintNV;3517} else if (gather) {3518if (parameters.Dref)3519if (sparse)3520opCode = Op::OpImageSparseDrefGather;3521else3522opCode = Op::OpImageDrefGather;3523else3524if (sparse)3525opCode = Op::OpImageSparseGather;3526else3527opCode = Op::OpImageGather;3528} else if (explicitLod) {3529if (parameters.Dref) {3530if (proj)3531if (sparse)3532opCode = Op::OpImageSparseSampleProjDrefExplicitLod;3533else3534opCode = Op::OpImageSampleProjDrefExplicitLod;3535else3536if (sparse)3537opCode = Op::OpImageSparseSampleDrefExplicitLod;3538else3539opCode = Op::OpImageSampleDrefExplicitLod;3540} else {3541if (proj)3542if (sparse)3543opCode = Op::OpImageSparseSampleProjExplicitLod;3544else3545opCode = Op::OpImageSampleProjExplicitLod;3546else3547if (sparse)3548opCode = Op::OpImageSparseSampleExplicitLod;3549else3550opCode = Op::OpImageSampleExplicitLod;3551}3552} else {3553if (parameters.Dref) {3554if (proj)3555if (sparse)3556opCode = Op::OpImageSparseSampleProjDrefImplicitLod;3557else3558opCode = Op::OpImageSampleProjDrefImplicitLod;3559else3560if (sparse)3561opCode = Op::OpImageSparseSampleDrefImplicitLod;3562else3563opCode = Op::OpImageSampleDrefImplicitLod;3564} else {3565if (proj)3566if (sparse)3567opCode = Op::OpImageSparseSampleProjImplicitLod;3568else3569opCode = Op::OpImageSampleProjImplicitLod;3570else3571if (sparse)3572opCode = Op::OpImageSparseSampleImplicitLod;3573else3574opCode = Op::OpImageSampleImplicitLod;3575}3576}35773578// See if the result type is expecting a smeared result.3579// This happens when a legacy shadow*() call is made, which3580// gets a vec4 back instead of a float.3581Id smearedType = resultType;3582if (! isScalarType(resultType)) {3583switch (opCode) {3584case Op::OpImageSampleDrefImplicitLod:3585case Op::OpImageSampleDrefExplicitLod:3586case Op::OpImageSampleProjDrefImplicitLod:3587case Op::OpImageSampleProjDrefExplicitLod:3588resultType = getScalarTypeId(resultType);3589break;3590default:3591break;3592}3593}35943595Id typeId0 = 0;3596Id typeId1 = 0;35973598if (sparse) {3599typeId0 = resultType;3600typeId1 = getDerefTypeId(parameters.texelOut);3601resultType = makeStructResultType(typeId0, typeId1);3602}36033604// Build the SPIR-V instruction3605Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode);3606textureInst->reserveOperands(optArgNum + (texArgs.size() - (optArgNum + 1)));3607for (size_t op = 0; op < optArgNum; ++op)3608textureInst->addIdOperand(texArgs[op]);3609if (optArgNum < texArgs.size())3610textureInst->addImmediateOperand(texArgs[optArgNum]);3611for (size_t op = optArgNum + 1; op < texArgs.size(); ++op)3612textureInst->addIdOperand(texArgs[op]);3613setPrecision(textureInst->getResultId(), precision);3614addInstruction(std::unique_ptr<Instruction>(textureInst));36153616Id resultId = textureInst->getResultId();36173618if (sparse) {3619// set capability3620addCapability(Capability::SparseResidency);36213622// Decode the return type that was a special structure3623createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut);3624resultId = createCompositeExtract(resultId, typeId0, 0);3625setPrecision(resultId, precision);3626} else {3627// When a smear is needed, do it, as per what was computed3628// above when resultType was changed to a scalar type.3629if (resultType != smearedType)3630resultId = smearScalar(precision, resultId, smearedType);3631}36323633return resultId;3634}36353636// Comments in header3637Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult)3638{3639// Figure out the result type3640Id resultType = 0;3641switch (opCode) {3642case Op::OpImageQuerySize:3643case Op::OpImageQuerySizeLod:3644{3645int numComponents = 0;3646switch (getTypeDimensionality(getImageType(parameters.sampler))) {3647case Dim::Dim1D:3648case Dim::Buffer:3649numComponents = 1;3650break;3651case Dim::Dim2D:3652case Dim::Cube:3653case Dim::Rect:3654case Dim::SubpassData:3655numComponents = 2;3656break;3657case Dim::Dim3D:3658numComponents = 3;3659break;36603661default:3662assert(0);3663break;3664}3665if (isArrayedImageType(getImageType(parameters.sampler)))3666++numComponents;36673668Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32);3669if (numComponents == 1)3670resultType = intType;3671else3672resultType = makeVectorType(intType, numComponents);36733674break;3675}3676case Op::OpImageQueryLod:3677resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2);3678break;3679case Op::OpImageQueryLevels:3680case Op::OpImageQuerySamples:3681resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32);3682break;3683default:3684assert(0);3685break;3686}36873688Instruction* query = new Instruction(getUniqueId(), resultType, opCode);3689query->addIdOperand(parameters.sampler);3690if (parameters.coords)3691query->addIdOperand(parameters.coords);3692if (parameters.lod)3693query->addIdOperand(parameters.lod);3694addInstruction(std::unique_ptr<Instruction>(query));3695addCapability(Capability::ImageQuery);36963697return query->getResultId();3698}36993700// External comments in header.3701// Operates recursively to visit the composite's hierarchy.3702Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal)3703{3704Id boolType = makeBoolType();3705Id valueType = getTypeId(value1);37063707Id resultId = NoResult;37083709int numConstituents = getNumTypeConstituents(valueType);37103711// Scalars and Vectors37123713if (isScalarType(valueType) || isVectorType(valueType)) {3714assert(valueType == getTypeId(value2));3715// These just need a single comparison, just have3716// to figure out what it is.3717Op op;3718switch (getMostBasicTypeClass(valueType)) {3719case Op::OpTypeFloat:3720op = equal ? Op::OpFOrdEqual : Op::OpFUnordNotEqual;3721break;3722case Op::OpTypeInt:3723default:3724op = equal ? Op::OpIEqual : Op::OpINotEqual;3725break;3726case Op::OpTypeBool:3727op = equal ? Op::OpLogicalEqual : Op::OpLogicalNotEqual;3728precision = NoPrecision;3729break;3730}37313732if (isScalarType(valueType)) {3733// scalar3734resultId = createBinOp(op, boolType, value1, value2);3735} else {3736// vector3737resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2);3738setPrecision(resultId, precision);3739// reduce vector compares...3740resultId = createUnaryOp(equal ? Op::OpAll : Op::OpAny, boolType, resultId);3741}37423743return setPrecision(resultId, precision);3744}37453746// Only structs, arrays, and matrices should be left.3747// They share in common the reduction operation across their constituents.3748assert(isAggregateType(valueType) || isMatrixType(valueType));37493750// Compare each pair of constituents3751for (int constituent = 0; constituent < numConstituents; ++constituent) {3752std::vector<unsigned> indexes(1, constituent);3753Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent);3754Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent);3755Id constituent1 = createCompositeExtract(value1, constituentType1, indexes);3756Id constituent2 = createCompositeExtract(value2, constituentType2, indexes);37573758Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal);37593760if (constituent == 0)3761resultId = subResultId;3762else3763resultId = setPrecision(createBinOp(equal ? Op::OpLogicalAnd : Op::OpLogicalOr, boolType, resultId, subResultId),3764precision);3765}37663767return resultId;3768}37693770// OpCompositeConstruct3771Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents)3772{3773assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 &&3774getNumTypeConstituents(typeId) == constituents.size()) ||3775(isCooperativeVectorType(typeId) && constituents.size() == 1));37763777if (generatingOpCodeForSpecConst) {3778// Sometime, even in spec-constant-op mode, the constant composite to be3779// constructed may not be a specialization constant.3780// e.g.:3781// const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const);3782// The first column vector should be a spec constant one, as a_spec_const is a spec constant.3783// The second column vector should NOT be spec constant, as it does not contain any spec constants.3784// To handle such cases, we check the constituents of the constant vector to determine whether this3785// vector should be created as a spec constant.3786return makeCompositeConstant(typeId, constituents,3787std::any_of(constituents.begin(), constituents.end(),3788[&](spv::Id id) { return isSpecConstant(id); }));3789}37903791bool replicate = false;3792size_t numConstituents = constituents.size();37933794if (useReplicatedComposites || isCooperativeVectorType(typeId)) {3795replicate = numConstituents > 0 &&3796std::equal(constituents.begin() + 1, constituents.end(), constituents.begin());3797}37983799if (replicate) {3800numConstituents = 1;3801addCapability(spv::Capability::ReplicatedCompositesEXT);3802addExtension(spv::E_SPV_EXT_replicated_composites);3803}38043805Op opcode = replicate ? Op::OpCompositeConstructReplicateEXT : Op::OpCompositeConstruct;38063807Instruction* op = new Instruction(getUniqueId(), typeId, opcode);3808op->reserveOperands(constituents.size());3809for (size_t c = 0; c < numConstituents; ++c)3810op->addIdOperand(constituents[c]);3811addInstruction(std::unique_ptr<Instruction>(op));38123813return op->getResultId();3814}38153816// coopmat conversion3817Id Builder::createCooperativeMatrixConversion(Id typeId, Id source)3818{3819Instruction* op = new Instruction(getUniqueId(), typeId, Op::OpCooperativeMatrixConvertNV);3820op->addIdOperand(source);3821addInstruction(std::unique_ptr<Instruction>(op));38223823return op->getResultId();3824}38253826// coopmat reduce3827Id Builder::createCooperativeMatrixReduce(Op opcode, Id typeId, Id source, unsigned int mask, Id func)3828{3829Instruction* op = new Instruction(getUniqueId(), typeId, opcode);3830op->addIdOperand(source);3831op->addImmediateOperand(mask);3832op->addIdOperand(func);3833addInstruction(std::unique_ptr<Instruction>(op));38343835return op->getResultId();3836}38373838// coopmat per-element operation3839Id Builder::createCooperativeMatrixPerElementOp(Id typeId, const std::vector<Id>& operands)3840{3841Instruction* op = new Instruction(getUniqueId(), typeId, spv::Op::OpCooperativeMatrixPerElementOpNV);3842// skip operand[0], which is where the result is stored3843for (uint32_t i = 1; i < operands.size(); ++i) {3844op->addIdOperand(operands[i]);3845}3846addInstruction(std::unique_ptr<Instruction>(op));38473848return op->getResultId();3849}38503851// Vector or scalar constructor3852Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)3853{3854Id result = NoResult;3855unsigned int numTargetComponents = getNumTypeComponents(resultTypeId);3856unsigned int targetComponent = 0;38573858// Special case: when calling a vector constructor with a single scalar3859// argument, smear the scalar3860if (sources.size() == 1 && isScalar(sources[0]) && (numTargetComponents > 1 || isCooperativeVectorType(resultTypeId)))3861return smearScalar(precision, sources[0], resultTypeId);38623863// Special case: 2 vectors of equal size3864if (sources.size() == 1 && isVector(sources[0]) && numTargetComponents == getNumComponents(sources[0])) {3865assert(resultTypeId == getTypeId(sources[0]));3866return sources[0];3867}38683869// accumulate the arguments for OpCompositeConstruct3870std::vector<Id> constituents;3871Id scalarTypeId = getScalarTypeId(resultTypeId);38723873// lambda to store the result of visiting an argument component3874const auto latchResult = [&](Id comp) {3875if (numTargetComponents > 1)3876constituents.push_back(comp);3877else3878result = comp;3879++targetComponent;3880};38813882// lambda to visit a vector argument's components3883const auto accumulateVectorConstituents = [&](Id sourceArg) {3884unsigned int sourceSize = getNumComponents(sourceArg);3885unsigned int sourcesToUse = sourceSize;3886if (sourcesToUse + targetComponent > numTargetComponents)3887sourcesToUse = numTargetComponents - targetComponent;38883889for (unsigned int s = 0; s < sourcesToUse; ++s) {3890std::vector<unsigned> swiz;3891swiz.push_back(s);3892latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz));3893}3894};38953896// lambda to visit a matrix argument's components3897const auto accumulateMatrixConstituents = [&](Id sourceArg) {3898unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg);3899unsigned int sourcesToUse = sourceSize;3900if (sourcesToUse + targetComponent > numTargetComponents)3901sourcesToUse = numTargetComponents - targetComponent;39023903unsigned int col = 0;3904unsigned int row = 0;3905for (unsigned int s = 0; s < sourcesToUse; ++s) {3906if (row >= getNumRows(sourceArg)) {3907row = 0;3908col++;3909}3910std::vector<Id> indexes;3911indexes.push_back(col);3912indexes.push_back(row);3913latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes));3914row++;3915}3916};39173918// Go through the source arguments, each one could have either3919// a single or multiple components to contribute.3920for (unsigned int i = 0; i < sources.size(); ++i) {39213922if (isScalar(sources[i]) || isPointer(sources[i]))3923latchResult(sources[i]);3924else if (isVector(sources[i]) || isCooperativeVector(sources[i]))3925accumulateVectorConstituents(sources[i]);3926else if (isMatrix(sources[i]))3927accumulateMatrixConstituents(sources[i]);3928else3929assert(0);39303931if (targetComponent >= numTargetComponents)3932break;3933}39343935// If the result is a vector, make it from the gathered constituents.3936if (constituents.size() > 0) {3937result = createCompositeConstruct(resultTypeId, constituents);3938return setPrecision(result, precision);3939} else {3940// Precision was set when generating this component.3941return result;3942}3943}39443945// Comments in header3946Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)3947{3948Id componentTypeId = getScalarTypeId(resultTypeId);3949unsigned int numCols = getTypeNumColumns(resultTypeId);3950unsigned int numRows = getTypeNumRows(resultTypeId);39513952Instruction* instr = module.getInstruction(componentTypeId);3953const unsigned bitCount = instr->getImmediateOperand(0);39543955// Optimize matrix constructed from a bigger matrix3956if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) {3957// To truncate the matrix to a smaller number of rows/columns, we need to:3958// 1. For each column, extract the column and truncate it to the required size using shuffle3959// 2. Assemble the resulting matrix from all columns3960Id matrix = sources[0];3961Id columnTypeId = getContainedTypeId(resultTypeId);3962Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix));39633964std::vector<unsigned> channels;3965for (unsigned int row = 0; row < numRows; ++row)3966channels.push_back(row);39673968std::vector<Id> matrixColumns;3969for (unsigned int col = 0; col < numCols; ++col) {3970std::vector<unsigned> indexes;3971indexes.push_back(col);3972Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes);3973setPrecision(colv, precision);39743975if (numRows != getNumRows(matrix)) {3976matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels));3977} else {3978matrixColumns.push_back(colv);3979}3980}39813982return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);3983}39843985// Detect a matrix being constructed from a repeated vector of the correct size.3986// Create the composite directly from it.3987if (sources.size() == numCols && isVector(sources[0]) && getNumComponents(sources[0]) == numRows &&3988std::equal(sources.begin() + 1, sources.end(), sources.begin())) {3989return setPrecision(createCompositeConstruct(resultTypeId, sources), precision);3990}39913992// Otherwise, will use a two step process3993// 1. make a compile-time 2D array of values3994// 2. construct a matrix from that array39953996// Step 1.39973998// initialize the array to the identity matrix3999Id ids[maxMatrixSize][maxMatrixSize];4000Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0));4001Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0));4002for (int col = 0; col < 4; ++col) {4003for (int row = 0; row < 4; ++row) {4004if (col == row)4005ids[col][row] = one;4006else4007ids[col][row] = zero;4008}4009}40104011// modify components as dictated by the arguments4012if (sources.size() == 1 && isScalar(sources[0])) {4013// a single scalar; resets the diagonals4014for (int col = 0; col < 4; ++col)4015ids[col][col] = sources[0];4016} else if (isMatrix(sources[0])) {4017// constructing from another matrix; copy over the parts that exist in both the argument and constructee4018Id matrix = sources[0];4019unsigned int minCols = std::min(numCols, getNumColumns(matrix));4020unsigned int minRows = std::min(numRows, getNumRows(matrix));4021for (unsigned int col = 0; col < minCols; ++col) {4022std::vector<unsigned> indexes;4023indexes.push_back(col);4024for (unsigned int row = 0; row < minRows; ++row) {4025indexes.push_back(row);4026ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes);4027indexes.pop_back();4028setPrecision(ids[col][row], precision);4029}4030}4031} else {4032// fill in the matrix in column-major order with whatever argument components are available4033unsigned int row = 0;4034unsigned int col = 0;40354036for (unsigned int arg = 0; arg < sources.size() && col < numCols; ++arg) {4037Id argComp = sources[arg];4038for (unsigned int comp = 0; comp < getNumComponents(sources[arg]); ++comp) {4039if (getNumComponents(sources[arg]) > 1) {4040argComp = createCompositeExtract(sources[arg], componentTypeId, comp);4041setPrecision(argComp, precision);4042}4043ids[col][row++] = argComp;4044if (row == numRows) {4045row = 0;4046col++;4047}4048if (col == numCols) {4049// If more components are provided than fit the matrix, discard the rest.4050break;4051}4052}4053}4054}40554056// Step 2: Construct a matrix from that array.4057// First make the column vectors, then make the matrix.40584059// make the column vectors4060Id columnTypeId = getContainedTypeId(resultTypeId);4061std::vector<Id> matrixColumns;4062for (unsigned int col = 0; col < numCols; ++col) {4063std::vector<Id> vectorComponents;4064for (unsigned int row = 0; row < numRows; ++row)4065vectorComponents.push_back(ids[col][row]);4066Id column = createCompositeConstruct(columnTypeId, vectorComponents);4067setPrecision(column, precision);4068matrixColumns.push_back(column);4069}40704071// make the matrix4072return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);4073}40744075// Comments in header4076Builder::If::If(Id cond, SelectionControlMask ctrl, Builder& gb) :4077builder(gb),4078condition(cond),4079control(ctrl),4080elseBlock(nullptr)4081{4082function = &builder.getBuildPoint()->getParent();40834084// make the blocks, but only put the then-block into the function,4085// the else-block and merge-block will be added later, in order, after4086// earlier code is emitted4087thenBlock = new Block(builder.getUniqueId(), *function);4088mergeBlock = new Block(builder.getUniqueId(), *function);40894090// Save the current block, so that we can add in the flow control split when4091// makeEndIf is called.4092headerBlock = builder.getBuildPoint();4093builder.createSelectionMerge(mergeBlock, control);40944095function->addBlock(thenBlock);4096builder.setBuildPoint(thenBlock);4097}40984099// Comments in header4100void Builder::If::makeBeginElse()4101{4102// Close out the "then" by having it jump to the mergeBlock4103builder.createBranch(true, mergeBlock);41044105// Make the first else block and add it to the function4106elseBlock = new Block(builder.getUniqueId(), *function);4107function->addBlock(elseBlock);41084109// Start building the else block4110builder.setBuildPoint(elseBlock);4111}41124113// Comments in header4114void Builder::If::makeEndIf()4115{4116// jump to the merge block4117builder.createBranch(true, mergeBlock);41184119// Go back to the headerBlock and make the flow control split4120builder.setBuildPoint(headerBlock);4121if (elseBlock)4122builder.createConditionalBranch(condition, thenBlock, elseBlock);4123else4124builder.createConditionalBranch(condition, thenBlock, mergeBlock);41254126// add the merge block to the function4127function->addBlock(mergeBlock);4128builder.setBuildPoint(mergeBlock);4129}41304131// Comments in header4132void Builder::makeSwitch(Id selector, SelectionControlMask control, int numSegments, const std::vector<int>& caseValues,4133const std::vector<int>& valueIndexToSegment, int defaultSegment,4134std::vector<Block*>& segmentBlocks)4135{4136Function& function = buildPoint->getParent();41374138// make all the blocks4139for (int s = 0; s < numSegments; ++s)4140segmentBlocks.push_back(new Block(getUniqueId(), function));41414142Block* mergeBlock = new Block(getUniqueId(), function);41434144// make and insert the switch's selection-merge instruction4145createSelectionMerge(mergeBlock, control);41464147// make the switch instruction4148Instruction* switchInst = new Instruction(NoResult, NoType, Op::OpSwitch);4149switchInst->reserveOperands((caseValues.size() * 2) + 2);4150switchInst->addIdOperand(selector);4151auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock;4152switchInst->addIdOperand(defaultOrMerge->getId());4153defaultOrMerge->addPredecessor(buildPoint);4154for (int i = 0; i < (int)caseValues.size(); ++i) {4155switchInst->addImmediateOperand(caseValues[i]);4156switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId());4157segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint);4158}4159addInstruction(std::unique_ptr<Instruction>(switchInst));41604161// push the merge block4162switchMerges.push(mergeBlock);4163}41644165// Comments in header4166void Builder::addSwitchBreak(bool implicit)4167{4168// branch to the top of the merge block stack4169createBranch(implicit, switchMerges.top());4170createAndSetNoPredecessorBlock("post-switch-break");4171}41724173// Comments in header4174void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment)4175{4176int lastSegment = nextSegment - 1;4177if (lastSegment >= 0) {4178// Close out previous segment by jumping, if necessary, to next segment4179if (! buildPoint->isTerminated())4180createBranch(true, segmentBlock[nextSegment]);4181}4182Block* block = segmentBlock[nextSegment];4183block->getParent().addBlock(block);4184setBuildPoint(block);4185}41864187// Comments in header4188void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/)4189{4190// Close out previous segment by jumping, if necessary, to next segment4191if (! buildPoint->isTerminated())4192addSwitchBreak(true);41934194switchMerges.top()->getParent().addBlock(switchMerges.top());4195setBuildPoint(switchMerges.top());41964197switchMerges.pop();4198}41994200Block& Builder::makeNewBlock()4201{4202Function& function = buildPoint->getParent();4203auto block = new Block(getUniqueId(), function);4204function.addBlock(block);4205return *block;4206}42074208Builder::LoopBlocks& Builder::makeNewLoop()4209{4210// This verbosity is needed to simultaneously get the same behavior4211// everywhere (id's in the same order), have a syntax that works4212// across lots of versions of C++, have no warnings from pedantic4213// compilation modes, and leave the rest of the code alone.4214Block& head = makeNewBlock();4215Block& body = makeNewBlock();4216Block& merge = makeNewBlock();4217Block& continue_target = makeNewBlock();4218LoopBlocks blocks(head, body, merge, continue_target);4219loops.push(blocks);4220return loops.top();4221}42224223void Builder::createLoopContinue()4224{4225createBranch(false, &loops.top().continue_target);4226// Set up a block for dead code.4227createAndSetNoPredecessorBlock("post-loop-continue");4228}42294230void Builder::createLoopExit()4231{4232createBranch(false, &loops.top().merge);4233// Set up a block for dead code.4234createAndSetNoPredecessorBlock("post-loop-break");4235}42364237void Builder::closeLoop()4238{4239loops.pop();4240}42414242void Builder::clearAccessChain()4243{4244accessChain.base = NoResult;4245accessChain.indexChain.clear();4246accessChain.instr = NoResult;4247accessChain.swizzle.clear();4248accessChain.component = NoResult;4249accessChain.preSwizzleBaseType = NoType;4250accessChain.isRValue = false;4251accessChain.coherentFlags.clear();4252accessChain.alignment = 0;4253}42544255// Comments in header4256void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,4257AccessChain::CoherentFlags coherentFlags, unsigned int alignment)4258{4259accessChain.coherentFlags |= coherentFlags;4260accessChain.alignment |= alignment;42614262// swizzles can be stacked in GLSL, but simplified to a single4263// one here; the base type doesn't change4264if (accessChain.preSwizzleBaseType == NoType)4265accessChain.preSwizzleBaseType = preSwizzleBaseType;42664267// if needed, propagate the swizzle for the current access chain4268if (accessChain.swizzle.size() > 0) {4269std::vector<unsigned> oldSwizzle = accessChain.swizzle;4270accessChain.swizzle.resize(0);4271for (unsigned int i = 0; i < swizzle.size(); ++i) {4272assert(swizzle[i] < oldSwizzle.size());4273accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]);4274}4275} else4276accessChain.swizzle = swizzle;42774278// determine if we need to track this swizzle anymore4279simplifyAccessChainSwizzle();4280}42814282// Comments in header4283void Builder::accessChainStore(Id rvalue, Decoration nonUniform, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment)4284{4285assert(accessChain.isRValue == false);42864287transferAccessChainSwizzle(true);42884289// If a swizzle exists and is not full and is not dynamic, then the swizzle will be broken into individual stores.4290if (accessChain.swizzle.size() > 0 &&4291getNumTypeComponents(getResultingAccessChainType()) != accessChain.swizzle.size() &&4292accessChain.component == NoResult) {4293for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {4294accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle[i]));4295accessChain.instr = NoResult;42964297Id base = collapseAccessChain();4298addDecoration(base, nonUniform);42994300accessChain.indexChain.pop_back();4301accessChain.instr = NoResult;43024303// dynamic component should be gone4304assert(accessChain.component == NoResult);43054306Id source = createCompositeExtract(rvalue, getContainedTypeId(getTypeId(rvalue)), i);43074308// take LSB of alignment4309alignment = alignment & ~(alignment & (alignment-1));4310if (getStorageClass(base) == StorageClass::PhysicalStorageBufferEXT) {4311memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessMask::Aligned);4312}43134314createStore(source, base, memoryAccess, scope, alignment);4315}4316}4317else {4318Id base = collapseAccessChain();4319addDecoration(base, nonUniform);43204321Id source = rvalue;43224323// dynamic component should be gone4324assert(accessChain.component == NoResult);43254326// If swizzle still exists, it may be out-of-order, we must load the target vector,4327// extract and insert elements to perform writeMask and/or swizzle.4328if (accessChain.swizzle.size() > 0) {4329Id tempBaseId = createLoad(base, spv::NoPrecision);4330source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle);4331}43324333// take LSB of alignment4334alignment = alignment & ~(alignment & (alignment-1));4335if (getStorageClass(base) == StorageClass::PhysicalStorageBufferEXT) {4336memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessMask::Aligned);4337}43384339createStore(source, base, memoryAccess, scope, alignment);4340}4341}43424343// Comments in header4344Id Builder::accessChainLoad(Decoration precision, Decoration l_nonUniform,4345Decoration r_nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess,4346spv::Scope scope, unsigned int alignment)4347{4348Id id;43494350if (accessChain.isRValue) {4351// transfer access chain, but try to stay in registers4352transferAccessChainSwizzle(false);4353if (accessChain.indexChain.size() > 0) {4354Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType;43554356// if all the accesses are constants, we can use OpCompositeExtract4357std::vector<unsigned> indexes;4358bool constant = true;4359for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {4360if (isConstantScalar(accessChain.indexChain[i]))4361indexes.push_back(getConstantScalar(accessChain.indexChain[i]));4362else {4363constant = false;4364break;4365}4366}43674368if (constant) {4369id = createCompositeExtract(accessChain.base, swizzleBase, indexes);4370setPrecision(id, precision);4371} else if (isCooperativeVector(accessChain.base)) {4372assert(accessChain.indexChain.size() == 1);4373id = createVectorExtractDynamic(accessChain.base, resultType, accessChain.indexChain[0]);4374} else {4375Id lValue = NoResult;4376if (spvVersion >= Spv_1_4 && isValidInitializer(accessChain.base)) {4377// make a new function variable for this r-value, using an initializer,4378// and mark it as NonWritable so that downstream it can be detected as a lookup4379// table4380lValue = createVariable(NoPrecision, StorageClass::Function, getTypeId(accessChain.base),4381"indexable", accessChain.base);4382addDecoration(lValue, Decoration::NonWritable);4383} else {4384lValue = createVariable(NoPrecision, StorageClass::Function, getTypeId(accessChain.base),4385"indexable");4386// store into it4387createStore(accessChain.base, lValue);4388}4389// move base to the new variable4390accessChain.base = lValue;4391accessChain.isRValue = false;43924393// load through the access chain4394id = createLoad(collapseAccessChain(), precision);4395}4396} else4397id = accessChain.base; // no precision, it was set when this was defined4398} else {4399transferAccessChainSwizzle(true);44004401// take LSB of alignment4402alignment = alignment & ~(alignment & (alignment-1));4403if (getStorageClass(accessChain.base) == StorageClass::PhysicalStorageBufferEXT) {4404memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessMask::Aligned);4405}44064407// load through the access chain4408id = collapseAccessChain();4409// Apply nonuniform both to the access chain and the loaded value.4410// Buffer accesses need the access chain decorated, and this is where4411// loaded image types get decorated. TODO: This should maybe move to4412// createImageTextureFunctionCall.4413addDecoration(id, l_nonUniform);4414id = createLoad(id, precision, memoryAccess, scope, alignment);4415addDecoration(id, r_nonUniform);4416}44174418// Done, unless there are swizzles to do4419if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)4420return id;44214422// Do remaining swizzling44234424// Do the basic swizzle4425if (accessChain.swizzle.size() > 0) {4426Id swizzledType = getScalarTypeId(getTypeId(id));4427if (accessChain.swizzle.size() > 1)4428swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size());4429id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle);4430}44314432// Do the dynamic component4433if (accessChain.component != NoResult)4434id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision);44354436addDecoration(id, r_nonUniform);4437return id;4438}44394440Id Builder::accessChainGetLValue()4441{4442assert(accessChain.isRValue == false);44434444transferAccessChainSwizzle(true);4445Id lvalue = collapseAccessChain();44464447// If swizzle exists, it is out-of-order or not full, we must load the target vector,4448// extract and insert elements to perform writeMask and/or swizzle. This does not4449// go with getting a direct l-value pointer.4450assert(accessChain.swizzle.size() == 0);4451assert(accessChain.component == NoResult);44524453return lvalue;4454}44554456// comment in header4457Id Builder::accessChainGetInferredType()4458{4459// anything to operate on?4460if (accessChain.base == NoResult)4461return NoType;4462Id type = getTypeId(accessChain.base);44634464// do initial dereference4465if (! accessChain.isRValue)4466type = getContainedTypeId(type);44674468// dereference each index4469for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) {4470if (isStructType(type))4471type = getContainedTypeId(type, getConstantScalar(*it));4472else4473type = getContainedTypeId(type);4474}44754476// dereference swizzle4477if (accessChain.swizzle.size() == 1)4478type = getContainedTypeId(type);4479else if (accessChain.swizzle.size() > 1)4480type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size());44814482// dereference component selection4483if (accessChain.component)4484type = getContainedTypeId(type);44854486return type;4487}44884489void Builder::dump(std::vector<unsigned int>& out) const4490{4491// Header, before first instructions:4492out.push_back(MagicNumber);4493out.push_back(spvVersion);4494out.push_back(builderNumber);4495out.push_back(uniqueId + 1);4496out.push_back(0);44974498// Capabilities4499for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) {4500Instruction capInst(0, 0, Op::OpCapability);4501capInst.addImmediateOperand(*it);4502capInst.dump(out);4503}45044505for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) {4506Instruction extInst(0, 0, Op::OpExtension);4507extInst.addStringOperand(it->c_str());4508extInst.dump(out);4509}45104511dumpInstructions(out, imports);4512Instruction memInst(0, 0, Op::OpMemoryModel);4513memInst.addImmediateOperand(addressModel);4514memInst.addImmediateOperand(memoryModel);4515memInst.dump(out);45164517// Instructions saved up while building:4518dumpInstructions(out, entryPoints);4519dumpInstructions(out, executionModes);45204521// Debug instructions4522dumpInstructions(out, strings);4523dumpSourceInstructions(out);4524for (int e = 0; e < (int)sourceExtensions.size(); ++e) {4525Instruction sourceExtInst(0, 0, Op::OpSourceExtension);4526sourceExtInst.addStringOperand(sourceExtensions[e]);4527sourceExtInst.dump(out);4528}4529dumpInstructions(out, names);4530dumpModuleProcesses(out);45314532// Annotation instructions4533dumpInstructions(out, decorations);45344535dumpInstructions(out, constantsTypesGlobals);4536dumpInstructions(out, externals);45374538// The functions4539module.dump(out);4540}45414542//4543// Protected methods.4544//45454546// Turn the described access chain in 'accessChain' into an instruction(s)4547// computing its address. This *cannot* include complex swizzles, which must4548// be handled after this is called.4549//4550// Can generate code.4551Id Builder::collapseAccessChain()4552{4553assert(accessChain.isRValue == false);45544555// did we already emit an access chain for this?4556if (accessChain.instr != NoResult)4557return accessChain.instr;45584559// If we have a dynamic component, we can still transfer4560// that into a final operand to the access chain. We need to remap the4561// dynamic component through the swizzle to get a new dynamic component to4562// update.4563//4564// This was not done in transferAccessChainSwizzle() because it might4565// generate code.4566remapDynamicSwizzle();4567if (accessChain.component != NoResult) {4568// transfer the dynamic component to the access chain4569accessChain.indexChain.push_back(accessChain.component);4570accessChain.component = NoResult;4571}45724573// note that non-trivial swizzling is left pending45744575// do we have an access chain?4576if (accessChain.indexChain.size() == 0)4577return accessChain.base;45784579// emit the access chain4580StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base));4581accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain);45824583return accessChain.instr;4584}45854586// For a dynamic component selection of a swizzle.4587//4588// Turn the swizzle and dynamic component into just a dynamic component.4589//4590// Generates code.4591void Builder::remapDynamicSwizzle()4592{4593// do we have a swizzle to remap a dynamic component through?4594if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) {4595// build a vector of the swizzle for the component to map into4596std::vector<Id> components;4597for (int c = 0; c < (int)accessChain.swizzle.size(); ++c)4598components.push_back(makeUintConstant(accessChain.swizzle[c]));4599Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size());4600Id map = makeCompositeConstant(mapType, components);46014602// use it4603accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component);4604accessChain.swizzle.clear();4605}4606}46074608// clear out swizzle if it is redundant, that is reselecting the same components4609// that would be present without the swizzle.4610void Builder::simplifyAccessChainSwizzle()4611{4612// If the swizzle has fewer components than the vector, it is subsetting, and must stay4613// to preserve that fact.4614if (getNumTypeComponents(accessChain.preSwizzleBaseType) > accessChain.swizzle.size())4615return;46164617// if components are out of order, it is a swizzle4618for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {4619if (i != accessChain.swizzle[i])4620return;4621}46224623// otherwise, there is no need to track this swizzle4624accessChain.swizzle.clear();4625if (accessChain.component == NoResult)4626accessChain.preSwizzleBaseType = NoType;4627}46284629// To the extent any swizzling can become part of the chain4630// of accesses instead of a post operation, make it so.4631// If 'dynamic' is true, include transferring the dynamic component,4632// otherwise, leave it pending.4633//4634// Does not generate code. just updates the access chain.4635void Builder::transferAccessChainSwizzle(bool dynamic)4636{4637// non existent?4638if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)4639return;46404641// too complex?4642// (this requires either a swizzle, or generating code for a dynamic component)4643if (accessChain.swizzle.size() > 1)4644return;46454646// single component, either in the swizzle and/or dynamic component4647if (accessChain.swizzle.size() == 1) {4648assert(accessChain.component == NoResult);4649// handle static component selection4650accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front()));4651accessChain.swizzle.clear();4652accessChain.preSwizzleBaseType = NoType;4653} else if (dynamic && accessChain.component != NoResult) {4654assert(accessChain.swizzle.size() == 0);4655// handle dynamic component4656accessChain.indexChain.push_back(accessChain.component);4657accessChain.preSwizzleBaseType = NoType;4658accessChain.component = NoResult;4659}4660}46614662// Utility method for creating a new block and setting the insert point to4663// be in it. This is useful for flow-control operations that need a "dummy"4664// block proceeding them (e.g. instructions after a discard, etc).4665void Builder::createAndSetNoPredecessorBlock(const char* /*name*/)4666{4667Block* block = new Block(getUniqueId(), buildPoint->getParent());4668block->setUnreachable();4669buildPoint->getParent().addBlock(block);4670setBuildPoint(block);46714672// if (name)4673// addName(block->getId(), name);4674}46754676// Comments in header4677void Builder::createBranch(bool implicit, Block* block)4678{4679Instruction* branch = new Instruction(Op::OpBranch);4680branch->addIdOperand(block->getId());4681if (implicit) {4682addInstructionNoDebugInfo(std::unique_ptr<Instruction>(branch));4683}4684else {4685addInstruction(std::unique_ptr<Instruction>(branch));4686}4687block->addPredecessor(buildPoint);4688}46894690void Builder::createSelectionMerge(Block* mergeBlock, SelectionControlMask control)4691{4692Instruction* merge = new Instruction(Op::OpSelectionMerge);4693merge->reserveOperands(2);4694merge->addIdOperand(mergeBlock->getId());4695merge->addImmediateOperand(control);4696addInstruction(std::unique_ptr<Instruction>(merge));4697}46984699void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, LoopControlMask control,4700const std::vector<unsigned int>& operands)4701{4702Instruction* merge = new Instruction(Op::OpLoopMerge);4703merge->reserveOperands(operands.size() + 3);4704merge->addIdOperand(mergeBlock->getId());4705merge->addIdOperand(continueBlock->getId());4706merge->addImmediateOperand(control);4707for (int op = 0; op < (int)operands.size(); ++op)4708merge->addImmediateOperand(operands[op]);4709addInstruction(std::unique_ptr<Instruction>(merge));4710}47114712void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock)4713{4714Instruction* branch = new Instruction(Op::OpBranchConditional);4715branch->reserveOperands(3);4716branch->addIdOperand(condition);4717branch->addIdOperand(thenBlock->getId());4718branch->addIdOperand(elseBlock->getId());47194720// A conditional branch is always attached to a condition expression4721addInstructionNoDebugInfo(std::unique_ptr<Instruction>(branch));47224723thenBlock->addPredecessor(buildPoint);4724elseBlock->addPredecessor(buildPoint);4725}47264727// OpSource4728// [OpSourceContinued]4729// ...4730void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text,4731std::vector<unsigned int>& out) const4732{4733const int maxWordCount = 0xFFFF;4734const int opSourceWordCount = 4;4735const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1;47364737if (sourceLang != SourceLanguage::Unknown) {4738// OpSource Language Version File Source4739Instruction sourceInst(NoResult, NoType, Op::OpSource);4740sourceInst.reserveOperands(3);4741sourceInst.addImmediateOperand(sourceLang);4742sourceInst.addImmediateOperand(sourceVersion);4743// File operand4744if (fileId != NoResult) {4745sourceInst.addIdOperand(fileId);4746// Source operand4747if (text.size() > 0) {4748int nextByte = 0;4749std::string subString;4750while ((int)text.size() - nextByte > 0) {4751subString = text.substr(nextByte, nonNullBytesPerInstruction);4752if (nextByte == 0) {4753// OpSource4754sourceInst.addStringOperand(subString.c_str());4755sourceInst.dump(out);4756} else {4757// OpSourcContinued4758Instruction sourceContinuedInst(Op::OpSourceContinued);4759sourceContinuedInst.addStringOperand(subString.c_str());4760sourceContinuedInst.dump(out);4761}4762nextByte += nonNullBytesPerInstruction;4763}4764} else4765sourceInst.dump(out);4766} else4767sourceInst.dump(out);4768}4769}47704771// Dump an OpSource[Continued] sequence for the source and every include file4772void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const4773{4774if (emitNonSemanticShaderDebugInfo) return;4775dumpSourceInstructions(mainFileId, sourceText, out);4776for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr)4777dumpSourceInstructions(iItr->first, *iItr->second, out);4778}47794780template <class Range> void Builder::dumpInstructions(std::vector<unsigned int>& out, const Range& instructions) const4781{4782for (const auto& inst : instructions) {4783inst->dump(out);4784}4785}47864787void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const4788{4789for (int i = 0; i < (int)moduleProcesses.size(); ++i) {4790Instruction moduleProcessed(Op::OpModuleProcessed);4791moduleProcessed.addStringOperand(moduleProcesses[i]);4792moduleProcessed.dump(out);4793}4794}47954796bool Builder::DecorationInstructionLessThan::operator()(const std::unique_ptr<Instruction>& lhs,4797const std::unique_ptr<Instruction>& rhs) const4798{4799// Order by the id to which the decoration applies first. This is more intuitive.4800assert(lhs->isIdOperand(0) && rhs->isIdOperand(0));4801if (lhs->getIdOperand(0) != rhs->getIdOperand(0)) {4802return lhs->getIdOperand(0) < rhs->getIdOperand(0);4803}48044805if (lhs->getOpCode() != rhs->getOpCode())4806return lhs->getOpCode() < rhs->getOpCode();48074808// Now compare the operands.4809int minSize = std::min(lhs->getNumOperands(), rhs->getNumOperands());4810for (int i = 1; i < minSize; ++i) {4811if (lhs->isIdOperand(i) != rhs->isIdOperand(i)) {4812return lhs->isIdOperand(i) < rhs->isIdOperand(i);4813}48144815if (lhs->isIdOperand(i)) {4816if (lhs->getIdOperand(i) != rhs->getIdOperand(i)) {4817return lhs->getIdOperand(i) < rhs->getIdOperand(i);4818}4819} else {4820if (lhs->getImmediateOperand(i) != rhs->getImmediateOperand(i)) {4821return lhs->getImmediateOperand(i) < rhs->getImmediateOperand(i);4822}4823}4824}48254826if (lhs->getNumOperands() != rhs->getNumOperands())4827return lhs->getNumOperands() < rhs->getNumOperands();48284829// In this case they are equal.4830return false;4831}4832} // end spv namespace483348344835