Path: blob/master/thirdparty/glslang/SPIRV/SpvBuilder.cpp
9906 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 "hex_float.h"4950#ifndef _WIN3251#include <cstdio>52#endif5354namespace spv {5556Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) :57spvVersion(spvVersion),58sourceLang(SourceLanguageUnknown),59sourceVersion(0),60addressModel(AddressingModelLogical),61memoryModel(MemoryModelGLSL450),62builderNumber(magicNumber),63buildPoint(nullptr),64uniqueId(0),65entryPointFunction(nullptr),66generatingOpCodeForSpecConst(false),67logger(buildLogger)68{69clearAccessChain();70}7172Builder::~Builder()73{74}7576Id Builder::import(const char* name)77{78Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport);79import->addStringOperand(name);80module.mapInstruction(import);8182imports.push_back(std::unique_ptr<Instruction>(import));83return import->getResultId();84}8586// For creating new groupedTypes (will return old type if the requested one was already made).87Id Builder::makeVoidType()88{89Instruction* type;90if (groupedTypes[OpTypeVoid].size() == 0) {91Id typeId = getUniqueId();92type = new Instruction(typeId, NoType, OpTypeVoid);93groupedTypes[OpTypeVoid].push_back(type);94constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));95module.mapInstruction(type);96// Core OpTypeVoid used for debug void type97if (emitNonSemanticShaderDebugInfo)98debugId[typeId] = typeId;99} else100type = groupedTypes[OpTypeVoid].back();101102return type->getResultId();103}104105Id Builder::makeBoolType()106{107Instruction* type;108if (groupedTypes[OpTypeBool].size() == 0) {109type = new Instruction(getUniqueId(), NoType, OpTypeBool);110groupedTypes[OpTypeBool].push_back(type);111constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));112module.mapInstruction(type);113114if (emitNonSemanticShaderDebugInfo) {115auto const debugResultId = makeBoolDebugType(32);116debugId[type->getResultId()] = debugResultId;117}118119} else120type = groupedTypes[OpTypeBool].back();121122123return type->getResultId();124}125126Id Builder::makeSamplerType()127{128Instruction* type;129if (groupedTypes[OpTypeSampler].size() == 0) {130type = new Instruction(getUniqueId(), NoType, OpTypeSampler);131groupedTypes[OpTypeSampler].push_back(type);132constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));133module.mapInstruction(type);134} else135type = groupedTypes[OpTypeSampler].back();136137if (emitNonSemanticShaderDebugInfo)138{139auto const debugResultId = makeCompositeDebugType({}, "type.sampler", NonSemanticShaderDebugInfo100Structure, true);140debugId[type->getResultId()] = debugResultId;141}142143return type->getResultId();144}145146Id Builder::makePointer(StorageClass storageClass, Id pointee)147{148// try to find it149Instruction* type;150for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {151type = groupedTypes[OpTypePointer][t];152if (type->getImmediateOperand(0) == (unsigned)storageClass &&153type->getIdOperand(1) == pointee)154return type->getResultId();155}156157// not found, make it158type = new Instruction(getUniqueId(), NoType, OpTypePointer);159type->reserveOperands(2);160type->addImmediateOperand(storageClass);161type->addIdOperand(pointee);162groupedTypes[OpTypePointer].push_back(type);163constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));164module.mapInstruction(type);165166if (emitNonSemanticShaderDebugInfo) {167const Id debugResultId = makePointerDebugType(storageClass, pointee);168debugId[type->getResultId()] = debugResultId;169}170171return type->getResultId();172}173174Id Builder::makeForwardPointer(StorageClass storageClass)175{176// Caching/uniquifying doesn't work here, because we don't know the177// pointee type and there can be multiple forward pointers of the same178// storage type. Somebody higher up in the stack must keep track.179Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer);180type->addImmediateOperand(storageClass);181constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));182module.mapInstruction(type);183184return type->getResultId();185}186187Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee)188{189// try to find it190Instruction* type;191for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {192type = groupedTypes[OpTypePointer][t];193if (type->getImmediateOperand(0) == (unsigned)storageClass &&194type->getIdOperand(1) == pointee)195return type->getResultId();196}197198type = new Instruction(forwardPointerType, NoType, OpTypePointer);199type->reserveOperands(2);200type->addImmediateOperand(storageClass);201type->addIdOperand(pointee);202groupedTypes[OpTypePointer].push_back(type);203constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));204module.mapInstruction(type);205206return type->getResultId();207}208209Id Builder::makeIntegerType(int width, bool hasSign)210{211// try to find it212Instruction* type;213for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) {214type = groupedTypes[OpTypeInt][t];215if (type->getImmediateOperand(0) == (unsigned)width &&216type->getImmediateOperand(1) == (hasSign ? 1u : 0u))217return type->getResultId();218}219220// not found, make it221type = new Instruction(getUniqueId(), NoType, OpTypeInt);222type->reserveOperands(2);223type->addImmediateOperand(width);224type->addImmediateOperand(hasSign ? 1 : 0);225groupedTypes[OpTypeInt].push_back(type);226constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));227module.mapInstruction(type);228229// deal with capabilities230switch (width) {231case 8:232case 16:233// these are currently handled by storage-type declarations and post processing234break;235case 64:236addCapability(CapabilityInt64);237break;238default:239break;240}241242if (emitNonSemanticShaderDebugInfo)243{244auto const debugResultId = makeIntegerDebugType(width, hasSign);245debugId[type->getResultId()] = debugResultId;246}247248return type->getResultId();249}250251Id Builder::makeFloatType(int width)252{253// try to find it254Instruction* type;255for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) {256type = groupedTypes[OpTypeFloat][t];257if (type->getImmediateOperand(0) == (unsigned)width)258return type->getResultId();259}260261// not found, make it262type = new Instruction(getUniqueId(), NoType, OpTypeFloat);263type->addImmediateOperand(width);264groupedTypes[OpTypeFloat].push_back(type);265constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));266module.mapInstruction(type);267268// deal with capabilities269switch (width) {270case 16:271// currently handled by storage-type declarations and post processing272break;273case 64:274addCapability(CapabilityFloat64);275break;276default:277break;278}279280if (emitNonSemanticShaderDebugInfo)281{282auto const debugResultId = makeFloatDebugType(width);283debugId[type->getResultId()] = debugResultId;284}285286return type->getResultId();287}288289// Make a struct without checking for duplication.290// See makeStructResultType() for non-decorated structs291// needed as the result of some instructions, which does292// check for duplicates.293Id Builder::makeStructType(const std::vector<Id>& members, const char* name, bool const compilerGenerated)294{295// Don't look for previous one, because in the general case,296// structs can be duplicated except for decorations.297298// not found, make it299Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct);300for (int op = 0; op < (int)members.size(); ++op)301type->addIdOperand(members[op]);302groupedTypes[OpTypeStruct].push_back(type);303constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));304module.mapInstruction(type);305addName(type->getResultId(), name);306307if (emitNonSemanticShaderDebugInfo && !compilerGenerated)308{309auto const debugResultId = makeCompositeDebugType(members, name, NonSemanticShaderDebugInfo100Structure);310debugId[type->getResultId()] = debugResultId;311}312313return type->getResultId();314}315316// Make a struct for the simple results of several instructions,317// checking for duplication.318Id Builder::makeStructResultType(Id type0, Id type1)319{320// try to find it321Instruction* type;322for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) {323type = groupedTypes[OpTypeStruct][t];324if (type->getNumOperands() != 2)325continue;326if (type->getIdOperand(0) != type0 ||327type->getIdOperand(1) != type1)328continue;329return type->getResultId();330}331332// not found, make it333std::vector<spv::Id> members;334members.push_back(type0);335members.push_back(type1);336337return makeStructType(members, "ResType");338}339340Id Builder::makeVectorType(Id component, int size)341{342// try to find it343Instruction* type;344for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) {345type = groupedTypes[OpTypeVector][t];346if (type->getIdOperand(0) == component &&347type->getImmediateOperand(1) == (unsigned)size)348return type->getResultId();349}350351// not found, make it352type = new Instruction(getUniqueId(), NoType, OpTypeVector);353type->reserveOperands(2);354type->addIdOperand(component);355type->addImmediateOperand(size);356groupedTypes[OpTypeVector].push_back(type);357constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));358module.mapInstruction(type);359360if (emitNonSemanticShaderDebugInfo)361{362auto const debugResultId = makeVectorDebugType(component, size);363debugId[type->getResultId()] = debugResultId;364}365366return type->getResultId();367}368369Id Builder::makeMatrixType(Id component, int cols, int rows)370{371assert(cols <= maxMatrixSize && rows <= maxMatrixSize);372373Id column = makeVectorType(component, rows);374375// try to find it376Instruction* type;377for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) {378type = groupedTypes[OpTypeMatrix][t];379if (type->getIdOperand(0) == column &&380type->getImmediateOperand(1) == (unsigned)cols)381return type->getResultId();382}383384// not found, make it385type = new Instruction(getUniqueId(), NoType, OpTypeMatrix);386type->reserveOperands(2);387type->addIdOperand(column);388type->addImmediateOperand(cols);389groupedTypes[OpTypeMatrix].push_back(type);390constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));391module.mapInstruction(type);392393if (emitNonSemanticShaderDebugInfo)394{395auto const debugResultId = makeMatrixDebugType(column, cols);396debugId[type->getResultId()] = debugResultId;397}398399return type->getResultId();400}401402Id Builder::makeCooperativeMatrixTypeKHR(Id component, Id scope, Id rows, Id cols, Id use)403{404// try to find it405Instruction* type;406for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixKHR].size(); ++t) {407type = groupedTypes[OpTypeCooperativeMatrixKHR][t];408if (type->getIdOperand(0) == component &&409type->getIdOperand(1) == scope &&410type->getIdOperand(2) == rows &&411type->getIdOperand(3) == cols &&412type->getIdOperand(4) == use)413return type->getResultId();414}415416// not found, make it417type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixKHR);418type->reserveOperands(5);419type->addIdOperand(component);420type->addIdOperand(scope);421type->addIdOperand(rows);422type->addIdOperand(cols);423type->addIdOperand(use);424groupedTypes[OpTypeCooperativeMatrixKHR].push_back(type);425constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));426module.mapInstruction(type);427428return type->getResultId();429}430431Id Builder::makeCooperativeMatrixTypeNV(Id component, Id scope, Id rows, Id cols)432{433// try to find it434Instruction* type;435for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixNV].size(); ++t) {436type = groupedTypes[OpTypeCooperativeMatrixNV][t];437if (type->getIdOperand(0) == component && type->getIdOperand(1) == scope && type->getIdOperand(2) == rows &&438type->getIdOperand(3) == cols)439return type->getResultId();440}441442// not found, make it443type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixNV);444type->reserveOperands(4);445type->addIdOperand(component);446type->addIdOperand(scope);447type->addIdOperand(rows);448type->addIdOperand(cols);449groupedTypes[OpTypeCooperativeMatrixNV].push_back(type);450constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));451module.mapInstruction(type);452453return type->getResultId();454}455456Id Builder::makeCooperativeMatrixTypeWithSameShape(Id component, Id otherType)457{458Instruction* instr = module.getInstruction(otherType);459if (instr->getOpCode() == OpTypeCooperativeMatrixNV) {460return makeCooperativeMatrixTypeNV(component, instr->getIdOperand(1), instr->getIdOperand(2), instr->getIdOperand(3));461} else {462assert(instr->getOpCode() == OpTypeCooperativeMatrixKHR);463return makeCooperativeMatrixTypeKHR(component, instr->getIdOperand(1), instr->getIdOperand(2), instr->getIdOperand(3), instr->getIdOperand(4));464}465}466467Id Builder::makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands)468{469// try to find it470Instruction* type;471for (int t = 0; t < (int)groupedTypes[opcode].size(); ++t) {472type = groupedTypes[opcode][t];473if (static_cast<size_t>(type->getNumOperands()) != operands.size())474continue; // Number mismatch, find next475476bool match = true;477for (int op = 0; match && op < (int)operands.size(); ++op) {478match = (operands[op].isId ? type->getIdOperand(op) : type->getImmediateOperand(op)) == operands[op].word;479}480if (match)481return type->getResultId();482}483484// not found, make it485type = new Instruction(getUniqueId(), NoType, opcode);486type->reserveOperands(operands.size());487for (size_t op = 0; op < operands.size(); ++op) {488if (operands[op].isId)489type->addIdOperand(operands[op].word);490else491type->addImmediateOperand(operands[op].word);492}493groupedTypes[opcode].push_back(type);494constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));495module.mapInstruction(type);496497return type->getResultId();498}499500// TODO: performance: track arrays per stride501// If a stride is supplied (non-zero) make an array.502// If no stride (0), reuse previous array types.503// 'size' is an Id of a constant or specialization constant of the array size504Id Builder::makeArrayType(Id element, Id sizeId, int stride)505{506Instruction* type;507if (stride == 0) {508// try to find existing type509for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) {510type = groupedTypes[OpTypeArray][t];511if (type->getIdOperand(0) == element &&512type->getIdOperand(1) == sizeId)513return type->getResultId();514}515}516517// not found, make it518type = new Instruction(getUniqueId(), NoType, OpTypeArray);519type->reserveOperands(2);520type->addIdOperand(element);521type->addIdOperand(sizeId);522groupedTypes[OpTypeArray].push_back(type);523constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));524module.mapInstruction(type);525526if (emitNonSemanticShaderDebugInfo)527{528auto const debugResultId = makeArrayDebugType(element, sizeId);529debugId[type->getResultId()] = debugResultId;530}531532return type->getResultId();533}534535Id Builder::makeRuntimeArray(Id element)536{537Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray);538type->addIdOperand(element);539constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));540module.mapInstruction(type);541542if (emitNonSemanticShaderDebugInfo)543{544auto const debugResultId = makeArrayDebugType(element, makeUintConstant(0));545debugId[type->getResultId()] = debugResultId;546}547548return type->getResultId();549}550551Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes)552{553// try to find it554Instruction* type;555for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) {556type = groupedTypes[OpTypeFunction][t];557if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1)558continue;559bool mismatch = false;560for (int p = 0; p < (int)paramTypes.size(); ++p) {561if (paramTypes[p] != type->getIdOperand(p + 1)) {562mismatch = true;563break;564}565}566if (! mismatch)567{568// If compiling HLSL, glslang will create a wrapper function around the entrypoint. Accordingly, a void(void)569// function type is created for the wrapper function. However, nonsemantic shader debug information is disabled570// while creating the HLSL wrapper. Consequently, if we encounter another void(void) function, we need to create571// the associated debug function type if it hasn't been created yet.572if(emitNonSemanticShaderDebugInfo && debugId[type->getResultId()] == 0) {573assert(sourceLang == spv::SourceLanguageHLSL);574assert(getTypeClass(returnType) == OpTypeVoid && paramTypes.size() == 0);575576Id debugTypeId = makeDebugFunctionType(returnType, {});577debugId[type->getResultId()] = debugTypeId;578}579return type->getResultId();580}581}582583// not found, make it584Id typeId = getUniqueId();585type = new Instruction(typeId, NoType, OpTypeFunction);586type->reserveOperands(paramTypes.size() + 1);587type->addIdOperand(returnType);588for (int p = 0; p < (int)paramTypes.size(); ++p)589type->addIdOperand(paramTypes[p]);590groupedTypes[OpTypeFunction].push_back(type);591constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));592module.mapInstruction(type);593594// make debug type and map it595if (emitNonSemanticShaderDebugInfo) {596Id debugTypeId = makeDebugFunctionType(returnType, paramTypes);597debugId[typeId] = debugTypeId;598}599600return type->getResultId();601}602603Id Builder::makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes)604{605assert(debugId[returnType] != 0);606607Id typeId = getUniqueId();608auto type = new Instruction(typeId, makeVoidType(), OpExtInst);609type->reserveOperands(paramTypes.size() + 4);610type->addIdOperand(nonSemanticShaderDebugInfo);611type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeFunction);612type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic));613type->addIdOperand(debugId[returnType]);614for (auto const paramType : paramTypes) {615if (isPointerType(paramType) || isArrayType(paramType)) {616type->addIdOperand(debugId[getContainedTypeId(paramType)]);617}618else {619type->addIdOperand(debugId[paramType]);620}621}622constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));623module.mapInstruction(type);624return typeId;625}626627Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled,628ImageFormat format)629{630assert(sampled == 1 || sampled == 2);631632// try to find it633Instruction* type;634for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) {635type = groupedTypes[OpTypeImage][t];636if (type->getIdOperand(0) == sampledType &&637type->getImmediateOperand(1) == (unsigned int)dim &&638type->getImmediateOperand(2) == ( depth ? 1u : 0u) &&639type->getImmediateOperand(3) == (arrayed ? 1u : 0u) &&640type->getImmediateOperand(4) == ( ms ? 1u : 0u) &&641type->getImmediateOperand(5) == sampled &&642type->getImmediateOperand(6) == (unsigned int)format)643return type->getResultId();644}645646// not found, make it647type = new Instruction(getUniqueId(), NoType, OpTypeImage);648type->reserveOperands(7);649type->addIdOperand(sampledType);650type->addImmediateOperand( dim);651type->addImmediateOperand( depth ? 1 : 0);652type->addImmediateOperand(arrayed ? 1 : 0);653type->addImmediateOperand( ms ? 1 : 0);654type->addImmediateOperand(sampled);655type->addImmediateOperand((unsigned int)format);656657groupedTypes[OpTypeImage].push_back(type);658constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));659module.mapInstruction(type);660661// deal with capabilities662switch (dim) {663case DimBuffer:664if (sampled == 1)665addCapability(CapabilitySampledBuffer);666else667addCapability(CapabilityImageBuffer);668break;669case Dim1D:670if (sampled == 1)671addCapability(CapabilitySampled1D);672else673addCapability(CapabilityImage1D);674break;675case DimCube:676if (arrayed) {677if (sampled == 1)678addCapability(CapabilitySampledCubeArray);679else680addCapability(CapabilityImageCubeArray);681}682break;683case DimRect:684if (sampled == 1)685addCapability(CapabilitySampledRect);686else687addCapability(CapabilityImageRect);688break;689case DimSubpassData:690addCapability(CapabilityInputAttachment);691break;692default:693break;694}695696if (ms) {697if (sampled == 2) {698// Images used with subpass data are not storage699// images, so don't require the capability for them.700if (dim != Dim::DimSubpassData)701addCapability(CapabilityStorageImageMultisample);702if (arrayed)703addCapability(CapabilityImageMSArray);704}705}706707if (emitNonSemanticShaderDebugInfo)708{709auto TypeName = [&dim]() -> char const* {710switch (dim) {711case Dim1D: return "type.1d.image";712case Dim2D: return "type.2d.image";713case Dim3D: return "type.3d.image";714case DimCube: return "type.cube.image";715default: return "type.image";716}717};718719auto const debugResultId = makeCompositeDebugType({}, TypeName(), NonSemanticShaderDebugInfo100Class, true);720debugId[type->getResultId()] = debugResultId;721}722723return type->getResultId();724}725726Id Builder::makeSampledImageType(Id imageType)727{728// try to find it729Instruction* type;730for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) {731type = groupedTypes[OpTypeSampledImage][t];732if (type->getIdOperand(0) == imageType)733return type->getResultId();734}735736// not found, make it737type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage);738type->addIdOperand(imageType);739740groupedTypes[OpTypeSampledImage].push_back(type);741constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));742module.mapInstruction(type);743744if (emitNonSemanticShaderDebugInfo)745{746auto const debugResultId = makeCompositeDebugType({}, "type.sampled.image", NonSemanticShaderDebugInfo100Class, true);747debugId[type->getResultId()] = debugResultId;748}749750return type->getResultId();751}752753Id Builder::makeDebugInfoNone()754{755if (debugInfoNone != 0)756return debugInfoNone;757758Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);759inst->reserveOperands(2);760inst->addIdOperand(nonSemanticShaderDebugInfo);761inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugInfoNone);762763constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));764module.mapInstruction(inst);765766debugInfoNone = inst->getResultId();767768return debugInfoNone;769}770771Id Builder::makeBoolDebugType(int const size)772{773// try to find it774Instruction* type;775for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) {776type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t];777if (type->getIdOperand(0) == getStringId("bool") &&778type->getIdOperand(1) == static_cast<unsigned int>(size) &&779type->getIdOperand(2) == NonSemanticShaderDebugInfo100Boolean)780return type->getResultId();781}782783type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);784type->reserveOperands(6);785type->addIdOperand(nonSemanticShaderDebugInfo);786type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic);787788type->addIdOperand(getStringId("bool")); // name id789type->addIdOperand(makeUintConstant(size)); // size id790type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Boolean)); // encoding id791type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id792793groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type);794constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));795module.mapInstruction(type);796797return type->getResultId();798}799800Id Builder::makeIntegerDebugType(int const width, bool const hasSign)801{802const char* typeName = nullptr;803switch (width) {804case 8: typeName = hasSign ? "int8_t" : "uint8_t"; break;805case 16: typeName = hasSign ? "int16_t" : "uint16_t"; break;806case 64: typeName = hasSign ? "int64_t" : "uint64_t"; break;807default: typeName = hasSign ? "int" : "uint";808}809auto nameId = getStringId(typeName);810// try to find it811Instruction* type;812for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) {813type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t];814if (type->getIdOperand(0) == nameId &&815type->getIdOperand(1) == static_cast<unsigned int>(width) &&816type->getIdOperand(2) == (hasSign ? NonSemanticShaderDebugInfo100Signed : NonSemanticShaderDebugInfo100Unsigned))817return type->getResultId();818}819820// not found, make it821type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);822type->reserveOperands(6);823type->addIdOperand(nonSemanticShaderDebugInfo);824type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic);825type->addIdOperand(nameId); // name id826type->addIdOperand(makeUintConstant(width)); // size id827if(hasSign == true) {828type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Signed)); // encoding id829} else {830type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Unsigned)); // encoding id831}832type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id833834groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type);835constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));836module.mapInstruction(type);837838return type->getResultId();839}840841Id Builder::makeFloatDebugType(int const width)842{843const char* typeName = nullptr;844switch (width) {845case 16: typeName = "float16_t"; break;846case 64: typeName = "double"; break;847default: typeName = "float"; break;848}849auto nameId = getStringId(typeName);850// try to find it851Instruction* type;852for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) {853type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t];854if (type->getIdOperand(0) == nameId &&855type->getIdOperand(1) == static_cast<unsigned int>(width) &&856type->getIdOperand(2) == NonSemanticShaderDebugInfo100Float)857return type->getResultId();858}859860// not found, make it861type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);862type->reserveOperands(6);863type->addIdOperand(nonSemanticShaderDebugInfo);864type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic);865type->addIdOperand(nameId); // name id866type->addIdOperand(makeUintConstant(width)); // size id867type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Float)); // encoding id868type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id869870groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type);871constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));872module.mapInstruction(type);873874return type->getResultId();875}876877Id Builder::makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfo100Instructions const sequenceType)878{879assert(sequenceType == NonSemanticShaderDebugInfo100DebugTypeArray ||880sequenceType == NonSemanticShaderDebugInfo100DebugTypeVector);881882// try to find it883Instruction* type;884for (int t = 0; t < (int)groupedDebugTypes[sequenceType].size(); ++t) {885type = groupedDebugTypes[sequenceType][t];886if (type->getIdOperand(0) == baseType &&887type->getIdOperand(1) == makeUintConstant(componentCount))888return type->getResultId();889}890891// not found, make it892type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);893type->reserveOperands(4);894type->addIdOperand(nonSemanticShaderDebugInfo);895type->addImmediateOperand(sequenceType);896type->addIdOperand(debugId[baseType]); // base type897type->addIdOperand(componentCount); // component count898899groupedDebugTypes[sequenceType].push_back(type);900constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));901module.mapInstruction(type);902903return type->getResultId();904}905906Id Builder::makeArrayDebugType(Id const baseType, Id const componentCount)907{908return makeSequentialDebugType(baseType, componentCount, NonSemanticShaderDebugInfo100DebugTypeArray);909}910911Id Builder::makeVectorDebugType(Id const baseType, int const componentCount)912{913return makeSequentialDebugType(baseType, makeUintConstant(componentCount), NonSemanticShaderDebugInfo100DebugTypeVector);914}915916Id Builder::makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor)917{918// try to find it919Instruction* type;920for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].size(); ++t) {921type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix][t];922if (type->getIdOperand(0) == vectorType &&923type->getIdOperand(1) == makeUintConstant(vectorCount))924return type->getResultId();925}926927// not found, make it928type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);929type->reserveOperands(5);930type->addIdOperand(nonSemanticShaderDebugInfo);931type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeMatrix);932type->addIdOperand(debugId[vectorType]); // vector type id933type->addIdOperand(makeUintConstant(vectorCount)); // component count id934type->addIdOperand(makeBoolConstant(columnMajor)); // column-major id935936groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].push_back(type);937constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));938module.mapInstruction(type);939940return type->getResultId();941}942943Id Builder::makeMemberDebugType(Id const memberType, DebugTypeLoc const& debugTypeLoc)944{945assert(debugId[memberType] != 0);946947Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);948type->reserveOperands(10);949type->addIdOperand(nonSemanticShaderDebugInfo);950type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeMember);951type->addIdOperand(getStringId(debugTypeLoc.name)); // name id952type->addIdOperand(debugId[memberType]); // type id953type->addIdOperand(makeDebugSource(currentFileId)); // source id954type->addIdOperand(makeUintConstant(debugTypeLoc.line)); // line id TODO: currentLine is always zero955type->addIdOperand(makeUintConstant(debugTypeLoc.column)); // TODO: column id956type->addIdOperand(makeUintConstant(0)); // TODO: offset id957type->addIdOperand(makeUintConstant(0)); // TODO: size id958type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id959960groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMember].push_back(type);961constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));962module.mapInstruction(type);963964return type->getResultId();965}966967// Note: To represent a source language opaque type, this instruction must have no Members operands, Size operand must be968// DebugInfoNone, and Name must start with @ to avoid clashes with user defined names.969Id Builder::makeCompositeDebugType(std::vector<Id> const& memberTypes, char const*const name,970NonSemanticShaderDebugInfo100DebugCompositeType const tag, bool const isOpaqueType)971{972// Create the debug member types.973std::vector<Id> memberDebugTypes;974for(auto const memberType : memberTypes) {975assert(debugTypeLocs.find(memberType) != debugTypeLocs.end());976977// There _should_ be debug types for all the member types but currently buffer references978// do not have member debug info generated.979if (debugId[memberType])980memberDebugTypes.emplace_back(makeMemberDebugType(memberType, debugTypeLocs[memberType]));981982// TODO: Need to rethink this method of passing location information.983// debugTypeLocs.erase(memberType);984}985986// Create The structure debug type.987Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);988type->reserveOperands(memberDebugTypes.size() + 11);989type->addIdOperand(nonSemanticShaderDebugInfo);990type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeComposite);991type->addIdOperand(getStringId(name)); // name id992type->addIdOperand(makeUintConstant(tag)); // tag id993type->addIdOperand(makeDebugSource(currentFileId)); // source id994type->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero?995type->addIdOperand(makeUintConstant(0)); // TODO: column id996type->addIdOperand(makeDebugCompilationUnit()); // scope id997if(isOpaqueType == true) {998// Prepend '@' to opaque types.999type->addIdOperand(getStringId('@' + std::string(name))); // linkage name id1000type->addIdOperand(makeDebugInfoNone()); // size id1001} else {1002type->addIdOperand(getStringId(name)); // linkage name id1003type->addIdOperand(makeUintConstant(0)); // TODO: size id1004}1005type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id1006assert(isOpaqueType == false || (isOpaqueType == true && memberDebugTypes.empty()));1007for(auto const memberDebugType : memberDebugTypes) {1008type->addIdOperand(memberDebugType);1009}10101011groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeComposite].push_back(type);1012constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1013module.mapInstruction(type);10141015return type->getResultId();1016}10171018Id Builder::makePointerDebugType(StorageClass storageClass, Id const baseType)1019{1020const Id debugBaseType = debugId[baseType];1021if (!debugBaseType) {1022return makeDebugInfoNone();1023}1024const Id scID = makeUintConstant(storageClass);1025for (Instruction* otherType : groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypePointer]) {1026if (otherType->getIdOperand(2) == debugBaseType &&1027otherType->getIdOperand(3) == scID) {1028return otherType->getResultId();1029}1030}10311032Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);1033type->reserveOperands(5);1034type->addIdOperand(nonSemanticShaderDebugInfo);1035type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypePointer);1036type->addIdOperand(debugBaseType);1037type->addIdOperand(scID);1038type->addIdOperand(makeUintConstant(0));10391040groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypePointer].push_back(type);1041constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1042module.mapInstruction(type);10431044return type->getResultId();1045}10461047Id Builder::makeDebugSource(const Id fileName) {1048if (debugSourceId.find(fileName) != debugSourceId.end())1049return debugSourceId[fileName];1050spv::Id resultId = getUniqueId();1051Instruction* sourceInst = new Instruction(resultId, makeVoidType(), OpExtInst);1052sourceInst->reserveOperands(3);1053sourceInst->addIdOperand(nonSemanticShaderDebugInfo);1054sourceInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugSource);1055sourceInst->addIdOperand(fileName);1056if (emitNonSemanticShaderDebugSource) {1057spv::Id sourceId = 0;1058if (fileName == mainFileId) {1059sourceId = getStringId(sourceText);1060} else {1061auto incItr = includeFiles.find(fileName);1062if (incItr != includeFiles.end()) {1063sourceId = getStringId(*incItr->second);1064}1065}10661067// We omit the optional source text item if not available in glslang1068if (sourceId != 0) {1069sourceInst->addIdOperand(sourceId);1070}1071}1072constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceInst));1073module.mapInstruction(sourceInst);1074debugSourceId[fileName] = resultId;1075return resultId;1076}10771078Id Builder::makeDebugCompilationUnit() {1079if (nonSemanticShaderCompilationUnitId != 0)1080return nonSemanticShaderCompilationUnitId;1081spv::Id resultId = getUniqueId();1082Instruction* sourceInst = new Instruction(resultId, makeVoidType(), OpExtInst);1083sourceInst->reserveOperands(6);1084sourceInst->addIdOperand(nonSemanticShaderDebugInfo);1085sourceInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugCompilationUnit);1086sourceInst->addIdOperand(makeUintConstant(1)); // TODO(greg-lunarg): Get rid of magic number1087sourceInst->addIdOperand(makeUintConstant(4)); // TODO(greg-lunarg): Get rid of magic number1088sourceInst->addIdOperand(makeDebugSource(mainFileId));1089sourceInst->addIdOperand(makeUintConstant(sourceLang));1090constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceInst));1091module.mapInstruction(sourceInst);1092nonSemanticShaderCompilationUnitId = resultId;10931094// We can reasonably assume that makeDebugCompilationUnit will be called before any of1095// debug-scope stack. Function scopes and lexical scopes will occur afterward.1096assert(currentDebugScopeId.empty());1097currentDebugScopeId.push(nonSemanticShaderCompilationUnitId);10981099return resultId;1100}11011102Id Builder::createDebugGlobalVariable(Id const type, char const*const name, Id const variable)1103{1104assert(type != 0);11051106Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);1107inst->reserveOperands(11);1108inst->addIdOperand(nonSemanticShaderDebugInfo);1109inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugGlobalVariable);1110inst->addIdOperand(getStringId(name)); // name id1111inst->addIdOperand(type); // type id1112inst->addIdOperand(makeDebugSource(currentFileId)); // source id1113inst->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero?1114inst->addIdOperand(makeUintConstant(0)); // TODO: column id1115inst->addIdOperand(makeDebugCompilationUnit()); // scope id1116inst->addIdOperand(getStringId(name)); // linkage name id1117inst->addIdOperand(variable); // variable id1118inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsDefinition)); // flags id11191120constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));1121module.mapInstruction(inst);11221123return inst->getResultId();1124}11251126Id Builder::createDebugLocalVariable(Id type, char const*const name, size_t const argNumber)1127{1128assert(name != nullptr);1129assert(!currentDebugScopeId.empty());11301131Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);1132inst->reserveOperands(9);1133inst->addIdOperand(nonSemanticShaderDebugInfo);1134inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLocalVariable);1135inst->addIdOperand(getStringId(name)); // name id1136inst->addIdOperand(type); // type id1137inst->addIdOperand(makeDebugSource(currentFileId)); // source id1138inst->addIdOperand(makeUintConstant(currentLine)); // line id1139inst->addIdOperand(makeUintConstant(0)); // TODO: column id1140inst->addIdOperand(currentDebugScopeId.top()); // scope id1141inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsLocal)); // flags id1142if(argNumber != 0) {1143inst->addIdOperand(makeUintConstant(argNumber));1144}11451146constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));1147module.mapInstruction(inst);11481149return inst->getResultId();1150}11511152Id Builder::makeDebugExpression()1153{1154if (debugExpression != 0)1155return debugExpression;11561157Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);1158inst->reserveOperands(2);1159inst->addIdOperand(nonSemanticShaderDebugInfo);1160inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugExpression);11611162constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));1163module.mapInstruction(inst);11641165debugExpression = inst->getResultId();11661167return debugExpression;1168}11691170Id Builder::makeDebugDeclare(Id const debugLocalVariable, Id const pointer)1171{1172Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);1173inst->reserveOperands(5);1174inst->addIdOperand(nonSemanticShaderDebugInfo);1175inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugDeclare);1176inst->addIdOperand(debugLocalVariable); // debug local variable id1177inst->addIdOperand(pointer); // pointer to local variable id1178inst->addIdOperand(makeDebugExpression()); // expression id1179addInstruction(std::unique_ptr<Instruction>(inst));11801181return inst->getResultId();1182}11831184Id Builder::makeDebugValue(Id const debugLocalVariable, Id const value)1185{1186Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);1187inst->reserveOperands(5);1188inst->addIdOperand(nonSemanticShaderDebugInfo);1189inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugValue);1190inst->addIdOperand(debugLocalVariable); // debug local variable id1191inst->addIdOperand(value); // value of local variable id1192inst->addIdOperand(makeDebugExpression()); // expression id1193addInstruction(std::unique_ptr<Instruction>(inst));11941195return inst->getResultId();1196}11971198Id Builder::makeAccelerationStructureType()1199{1200Instruction *type;1201if (groupedTypes[OpTypeAccelerationStructureKHR].size() == 0) {1202type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureKHR);1203groupedTypes[OpTypeAccelerationStructureKHR].push_back(type);1204constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1205module.mapInstruction(type);1206if (emitNonSemanticShaderDebugInfo) {1207spv::Id debugType = makeCompositeDebugType({}, "accelerationStructure", NonSemanticShaderDebugInfo100Structure, true);1208debugId[type->getResultId()] = debugType;1209}1210} else {1211type = groupedTypes[OpTypeAccelerationStructureKHR].back();1212}12131214return type->getResultId();1215}12161217Id Builder::makeRayQueryType()1218{1219Instruction *type;1220if (groupedTypes[OpTypeRayQueryKHR].size() == 0) {1221type = new Instruction(getUniqueId(), NoType, OpTypeRayQueryKHR);1222groupedTypes[OpTypeRayQueryKHR].push_back(type);1223constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1224module.mapInstruction(type);1225if (emitNonSemanticShaderDebugInfo) {1226spv::Id debugType = makeCompositeDebugType({}, "rayQuery", NonSemanticShaderDebugInfo100Structure, true);1227debugId[type->getResultId()] = debugType;1228}1229} else {1230type = groupedTypes[OpTypeRayQueryKHR].back();1231}12321233return type->getResultId();1234}12351236Id Builder::makeHitObjectNVType()1237{1238Instruction *type;1239if (groupedTypes[OpTypeHitObjectNV].size() == 0) {1240type = new Instruction(getUniqueId(), NoType, OpTypeHitObjectNV);1241groupedTypes[OpTypeHitObjectNV].push_back(type);1242constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));1243module.mapInstruction(type);1244} else {1245type = groupedTypes[OpTypeHitObjectNV].back();1246}12471248return type->getResultId();1249}12501251Id Builder::getDerefTypeId(Id resultId) const1252{1253Id typeId = getTypeId(resultId);1254assert(isPointerType(typeId));12551256return module.getInstruction(typeId)->getIdOperand(1);1257}12581259Op Builder::getMostBasicTypeClass(Id typeId) const1260{1261Instruction* instr = module.getInstruction(typeId);12621263Op typeClass = instr->getOpCode();1264switch (typeClass)1265{1266case OpTypeVector:1267case OpTypeMatrix:1268case OpTypeArray:1269case OpTypeRuntimeArray:1270return getMostBasicTypeClass(instr->getIdOperand(0));1271case OpTypePointer:1272return getMostBasicTypeClass(instr->getIdOperand(1));1273default:1274return typeClass;1275}1276}12771278int Builder::getNumTypeConstituents(Id typeId) const1279{1280Instruction* instr = module.getInstruction(typeId);12811282switch (instr->getOpCode())1283{1284case OpTypeBool:1285case OpTypeInt:1286case OpTypeFloat:1287case OpTypePointer:1288return 1;1289case OpTypeVector:1290case OpTypeMatrix:1291return instr->getImmediateOperand(1);1292case OpTypeArray:1293{1294Id lengthId = instr->getIdOperand(1);1295return module.getInstruction(lengthId)->getImmediateOperand(0);1296}1297case OpTypeStruct:1298return instr->getNumOperands();1299case OpTypeCooperativeMatrixKHR:1300case OpTypeCooperativeMatrixNV:1301// has only one constituent when used with OpCompositeConstruct.1302return 1;1303default:1304assert(0);1305return 1;1306}1307}13081309// Return the lowest-level type of scalar that an homogeneous composite is made out of.1310// Typically, this is just to find out if something is made out of ints or floats.1311// However, it includes returning a structure, if say, it is an array of structure.1312Id Builder::getScalarTypeId(Id typeId) const1313{1314Instruction* instr = module.getInstruction(typeId);13151316Op typeClass = instr->getOpCode();1317switch (typeClass)1318{1319case OpTypeVoid:1320case OpTypeBool:1321case OpTypeInt:1322case OpTypeFloat:1323case OpTypeStruct:1324return instr->getResultId();1325case OpTypeVector:1326case OpTypeMatrix:1327case OpTypeArray:1328case OpTypeRuntimeArray:1329case OpTypePointer:1330return getScalarTypeId(getContainedTypeId(typeId));1331default:1332assert(0);1333return NoResult;1334}1335}13361337// Return the type of 'member' of a composite.1338Id Builder::getContainedTypeId(Id typeId, int member) const1339{1340Instruction* instr = module.getInstruction(typeId);13411342Op typeClass = instr->getOpCode();1343switch (typeClass)1344{1345case OpTypeVector:1346case OpTypeMatrix:1347case OpTypeArray:1348case OpTypeRuntimeArray:1349case OpTypeCooperativeMatrixKHR:1350case OpTypeCooperativeMatrixNV:1351return instr->getIdOperand(0);1352case OpTypePointer:1353return instr->getIdOperand(1);1354case OpTypeStruct:1355return instr->getIdOperand(member);1356default:1357assert(0);1358return NoResult;1359}1360}13611362// Figure out the final resulting type of the access chain.1363Id Builder::getResultingAccessChainType() const1364{1365assert(accessChain.base != NoResult);1366Id typeId = getTypeId(accessChain.base);13671368assert(isPointerType(typeId));1369typeId = getContainedTypeId(typeId);13701371for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {1372if (isStructType(typeId)) {1373assert(isConstantScalar(accessChain.indexChain[i]));1374typeId = getContainedTypeId(typeId, getConstantScalar(accessChain.indexChain[i]));1375} else1376typeId = getContainedTypeId(typeId, accessChain.indexChain[i]);1377}13781379return typeId;1380}13811382// Return the immediately contained type of a given composite type.1383Id Builder::getContainedTypeId(Id typeId) const1384{1385return getContainedTypeId(typeId, 0);1386}13871388// Returns true if 'typeId' is or contains a scalar type declared with 'typeOp'1389// of width 'width'. The 'width' is only consumed for int and float types.1390// Returns false otherwise.1391bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const1392{1393const Instruction& instr = *module.getInstruction(typeId);13941395Op typeClass = instr.getOpCode();1396switch (typeClass)1397{1398case OpTypeInt:1399case OpTypeFloat:1400return typeClass == typeOp && instr.getImmediateOperand(0) == width;1401case OpTypeStruct:1402for (int m = 0; m < instr.getNumOperands(); ++m) {1403if (containsType(instr.getIdOperand(m), typeOp, width))1404return true;1405}1406return false;1407case OpTypePointer:1408return false;1409case OpTypeVector:1410case OpTypeMatrix:1411case OpTypeArray:1412case OpTypeRuntimeArray:1413return containsType(getContainedTypeId(typeId), typeOp, width);1414default:1415return typeClass == typeOp;1416}1417}14181419// return true if the type is a pointer to PhysicalStorageBufferEXT or an1420// contains such a pointer. These require restrict/aliased decorations.1421bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const1422{1423const Instruction& instr = *module.getInstruction(typeId);14241425Op typeClass = instr.getOpCode();1426switch (typeClass)1427{1428case OpTypePointer:1429return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT;1430case OpTypeArray:1431return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId));1432case OpTypeStruct:1433for (int m = 0; m < instr.getNumOperands(); ++m) {1434if (containsPhysicalStorageBufferOrArray(instr.getIdOperand(m)))1435return true;1436}1437return false;1438default:1439return false;1440}1441}14421443// See if a scalar constant of this type has already been created, so it1444// can be reused rather than duplicated. (Required by the specification).1445Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value)1446{1447Instruction* constant;1448for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {1449constant = groupedConstants[typeClass][i];1450if (constant->getOpCode() == opcode &&1451constant->getTypeId() == typeId &&1452constant->getImmediateOperand(0) == value)1453return constant->getResultId();1454}14551456return 0;1457}14581459// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64').1460Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2)1461{1462Instruction* constant;1463for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {1464constant = groupedConstants[typeClass][i];1465if (constant->getOpCode() == opcode &&1466constant->getTypeId() == typeId &&1467constant->getImmediateOperand(0) == v1 &&1468constant->getImmediateOperand(1) == v2)1469return constant->getResultId();1470}14711472return 0;1473}14741475// Return true if consuming 'opcode' means consuming a constant.1476// "constant" here means after final transform to executable code,1477// the value consumed will be a constant, so includes specialization.1478bool Builder::isConstantOpCode(Op opcode) const1479{1480switch (opcode) {1481case OpUndef:1482case OpConstantTrue:1483case OpConstantFalse:1484case OpConstant:1485case OpConstantComposite:1486case OpConstantSampler:1487case OpConstantNull:1488case OpSpecConstantTrue:1489case OpSpecConstantFalse:1490case OpSpecConstant:1491case OpSpecConstantComposite:1492case OpSpecConstantOp:1493return true;1494default:1495return false;1496}1497}14981499// Return true if consuming 'opcode' means consuming a specialization constant.1500bool Builder::isSpecConstantOpCode(Op opcode) const1501{1502switch (opcode) {1503case OpSpecConstantTrue:1504case OpSpecConstantFalse:1505case OpSpecConstant:1506case OpSpecConstantComposite:1507case OpSpecConstantOp:1508return true;1509default:1510return false;1511}1512}15131514Id Builder::makeNullConstant(Id typeId)1515{1516Instruction* constant;15171518// See if we already made it.1519Id existing = NoResult;1520for (int i = 0; i < (int)nullConstants.size(); ++i) {1521constant = nullConstants[i];1522if (constant->getTypeId() == typeId)1523existing = constant->getResultId();1524}15251526if (existing != NoResult)1527return existing;15281529// Make it1530Instruction* c = new Instruction(getUniqueId(), typeId, OpConstantNull);1531constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1532nullConstants.push_back(c);1533module.mapInstruction(c);15341535return c->getResultId();1536}15371538Id Builder::makeBoolConstant(bool b, bool specConstant)1539{1540Id typeId = makeBoolType();1541Instruction* constant;1542Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse);15431544// See if we already made it. Applies only to regular constants, because specialization constants1545// must remain distinct for the purpose of applying a SpecId decoration.1546if (! specConstant) {1547Id existing = 0;1548for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) {1549constant = groupedConstants[OpTypeBool][i];1550if (constant->getTypeId() == typeId && constant->getOpCode() == opcode)1551existing = constant->getResultId();1552}15531554if (existing)1555return existing;1556}15571558// Make it1559Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1560constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1561groupedConstants[OpTypeBool].push_back(c);1562module.mapInstruction(c);15631564return c->getResultId();1565}15661567Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant)1568{1569Op opcode = specConstant ? OpSpecConstant : OpConstant;15701571// See if we already made it. Applies only to regular constants, because specialization constants1572// must remain distinct for the purpose of applying a SpecId decoration.1573if (! specConstant) {1574Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value);1575if (existing)1576return existing;1577}15781579Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1580c->addImmediateOperand(value);1581constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1582groupedConstants[OpTypeInt].push_back(c);1583module.mapInstruction(c);15841585return c->getResultId();1586}15871588Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant)1589{1590Op opcode = specConstant ? OpSpecConstant : OpConstant;15911592unsigned op1 = value & 0xFFFFFFFF;1593unsigned op2 = value >> 32;15941595// See if we already made it. Applies only to regular constants, because specialization constants1596// must remain distinct for the purpose of applying a SpecId decoration.1597if (! specConstant) {1598Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2);1599if (existing)1600return existing;1601}16021603Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1604c->reserveOperands(2);1605c->addImmediateOperand(op1);1606c->addImmediateOperand(op2);1607constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1608groupedConstants[OpTypeInt].push_back(c);1609module.mapInstruction(c);16101611return c->getResultId();1612}16131614Id Builder::makeFloatConstant(float f, bool specConstant)1615{1616Op opcode = specConstant ? OpSpecConstant : OpConstant;1617Id typeId = makeFloatType(32);1618union { float fl; unsigned int ui; } u;1619u.fl = f;1620unsigned value = u.ui;16211622// See if we already made it. Applies only to regular constants, because specialization constants1623// must remain distinct for the purpose of applying a SpecId decoration.1624if (! specConstant) {1625Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);1626if (existing)1627return existing;1628}16291630Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1631c->addImmediateOperand(value);1632constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1633groupedConstants[OpTypeFloat].push_back(c);1634module.mapInstruction(c);16351636return c->getResultId();1637}16381639Id Builder::makeDoubleConstant(double d, bool specConstant)1640{1641Op opcode = specConstant ? OpSpecConstant : OpConstant;1642Id typeId = makeFloatType(64);1643union { double db; unsigned long long ull; } u;1644u.db = d;1645unsigned long long value = u.ull;1646unsigned op1 = value & 0xFFFFFFFF;1647unsigned op2 = value >> 32;16481649// See if we already made it. Applies only to regular constants, because specialization constants1650// must remain distinct for the purpose of applying a SpecId decoration.1651if (! specConstant) {1652Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2);1653if (existing)1654return existing;1655}16561657Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1658c->reserveOperands(2);1659c->addImmediateOperand(op1);1660c->addImmediateOperand(op2);1661constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1662groupedConstants[OpTypeFloat].push_back(c);1663module.mapInstruction(c);16641665return c->getResultId();1666}16671668Id Builder::makeFloat16Constant(float f16, bool specConstant)1669{1670Op opcode = specConstant ? OpSpecConstant : OpConstant;1671Id typeId = makeFloatType(16);16721673spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16);1674spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0);1675fVal.castTo(f16Val, spvutils::kRoundToZero);16761677unsigned value = f16Val.value().getAsFloat().get_value();16781679// See if we already made it. Applies only to regular constants, because specialization constants1680// must remain distinct for the purpose of applying a SpecId decoration.1681if (!specConstant) {1682Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);1683if (existing)1684return existing;1685}16861687Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1688c->addImmediateOperand(value);1689constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1690groupedConstants[OpTypeFloat].push_back(c);1691module.mapInstruction(c);16921693return c->getResultId();1694}16951696Id Builder::makeFpConstant(Id type, double d, bool specConstant)1697{1698const int width = getScalarTypeWidth(type);16991700assert(isFloatType(type));17011702switch (width) {1703case 16:1704return makeFloat16Constant((float)d, specConstant);1705case 32:1706return makeFloatConstant((float)d, specConstant);1707case 64:1708return makeDoubleConstant(d, specConstant);1709default:1710break;1711}17121713assert(false);1714return NoResult;1715}17161717Id Builder::importNonSemanticShaderDebugInfoInstructions()1718{1719assert(emitNonSemanticShaderDebugInfo == true);17201721if(nonSemanticShaderDebugInfo == 0)1722{1723this->addExtension(spv::E_SPV_KHR_non_semantic_info);1724nonSemanticShaderDebugInfo = this->import("NonSemantic.Shader.DebugInfo.100");1725}17261727return nonSemanticShaderDebugInfo;1728}17291730Id Builder::findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps)1731{1732Instruction* constant = nullptr;1733bool found = false;1734for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {1735constant = groupedConstants[typeClass][i];17361737if (constant->getTypeId() != typeId)1738continue;17391740// same contents?1741bool mismatch = false;1742for (int op = 0; op < constant->getNumOperands(); ++op) {1743if (constant->getIdOperand(op) != comps[op]) {1744mismatch = true;1745break;1746}1747}1748if (! mismatch) {1749found = true;1750break;1751}1752}17531754return found ? constant->getResultId() : NoResult;1755}17561757Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps)1758{1759Instruction* constant = nullptr;1760bool found = false;1761for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) {1762constant = groupedStructConstants[typeId][i];17631764// same contents?1765bool mismatch = false;1766for (int op = 0; op < constant->getNumOperands(); ++op) {1767if (constant->getIdOperand(op) != comps[op]) {1768mismatch = true;1769break;1770}1771}1772if (! mismatch) {1773found = true;1774break;1775}1776}17771778return found ? constant->getResultId() : NoResult;1779}17801781// Comments in header1782Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant)1783{1784Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite;1785assert(typeId);1786Op typeClass = getTypeClass(typeId);17871788switch (typeClass) {1789case OpTypeVector:1790case OpTypeArray:1791case OpTypeMatrix:1792case OpTypeCooperativeMatrixKHR:1793case OpTypeCooperativeMatrixNV:1794if (! specConstant) {1795Id existing = findCompositeConstant(typeClass, typeId, members);1796if (existing)1797return existing;1798}1799break;1800case OpTypeStruct:1801if (! specConstant) {1802Id existing = findStructConstant(typeId, members);1803if (existing)1804return existing;1805}1806break;1807default:1808assert(0);1809return makeFloatConstant(0.0);1810}18111812Instruction* c = new Instruction(getUniqueId(), typeId, opcode);1813c->reserveOperands(members.size());1814for (int op = 0; op < (int)members.size(); ++op)1815c->addIdOperand(members[op]);1816constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));1817if (typeClass == OpTypeStruct)1818groupedStructConstants[typeId].push_back(c);1819else1820groupedConstants[typeClass].push_back(c);1821module.mapInstruction(c);18221823return c->getResultId();1824}18251826Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name)1827{1828Instruction* entryPoint = new Instruction(OpEntryPoint);1829entryPoint->reserveOperands(3);1830entryPoint->addImmediateOperand(model);1831entryPoint->addIdOperand(function->getId());1832entryPoint->addStringOperand(name);18331834entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint));18351836return entryPoint;1837}18381839// Currently relying on the fact that all 'value' of interest are small non-negative values.1840void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3)1841{1842// entryPoint can be null if we are in compile-only mode1843if (!entryPoint)1844return;18451846Instruction* instr = new Instruction(OpExecutionMode);1847instr->reserveOperands(3);1848instr->addIdOperand(entryPoint->getId());1849instr->addImmediateOperand(mode);1850if (value1 >= 0)1851instr->addImmediateOperand(value1);1852if (value2 >= 0)1853instr->addImmediateOperand(value2);1854if (value3 >= 0)1855instr->addImmediateOperand(value3);18561857executionModes.push_back(std::unique_ptr<Instruction>(instr));1858}18591860void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector<unsigned>& literals)1861{1862// entryPoint can be null if we are in compile-only mode1863if (!entryPoint)1864return;18651866Instruction* instr = new Instruction(OpExecutionMode);1867instr->reserveOperands(literals.size() + 2);1868instr->addIdOperand(entryPoint->getId());1869instr->addImmediateOperand(mode);1870for (auto literal : literals)1871instr->addImmediateOperand(literal);18721873executionModes.push_back(std::unique_ptr<Instruction>(instr));1874}18751876void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector<Id>& operandIds)1877{1878// entryPoint can be null if we are in compile-only mode1879if (!entryPoint)1880return;18811882Instruction* instr = new Instruction(OpExecutionModeId);1883instr->reserveOperands(operandIds.size() + 2);1884instr->addIdOperand(entryPoint->getId());1885instr->addImmediateOperand(mode);1886for (auto operandId : operandIds)1887instr->addIdOperand(operandId);18881889executionModes.push_back(std::unique_ptr<Instruction>(instr));1890}18911892void Builder::addName(Id id, const char* string)1893{1894Instruction* name = new Instruction(OpName);1895name->reserveOperands(2);1896name->addIdOperand(id);1897name->addStringOperand(string);18981899names.push_back(std::unique_ptr<Instruction>(name));1900}19011902void Builder::addMemberName(Id id, int memberNumber, const char* string)1903{1904Instruction* name = new Instruction(OpMemberName);1905name->reserveOperands(3);1906name->addIdOperand(id);1907name->addImmediateOperand(memberNumber);1908name->addStringOperand(string);19091910names.push_back(std::unique_ptr<Instruction>(name));1911}19121913void Builder::addDecoration(Id id, Decoration decoration, int num)1914{1915if (decoration == spv::DecorationMax)1916return;19171918Instruction* dec = new Instruction(OpDecorate);1919dec->reserveOperands(2);1920dec->addIdOperand(id);1921dec->addImmediateOperand(decoration);1922if (num >= 0)1923dec->addImmediateOperand(num);19241925decorations.push_back(std::unique_ptr<Instruction>(dec));1926}19271928void Builder::addDecoration(Id id, Decoration decoration, const char* s)1929{1930if (decoration == spv::DecorationMax)1931return;19321933Instruction* dec = new Instruction(OpDecorateString);1934dec->reserveOperands(3);1935dec->addIdOperand(id);1936dec->addImmediateOperand(decoration);1937dec->addStringOperand(s);19381939decorations.push_back(std::unique_ptr<Instruction>(dec));1940}19411942void Builder::addDecoration(Id id, Decoration decoration, const std::vector<unsigned>& literals)1943{1944if (decoration == spv::DecorationMax)1945return;19461947Instruction* dec = new Instruction(OpDecorate);1948dec->reserveOperands(literals.size() + 2);1949dec->addIdOperand(id);1950dec->addImmediateOperand(decoration);1951for (auto literal : literals)1952dec->addImmediateOperand(literal);19531954decorations.push_back(std::unique_ptr<Instruction>(dec));1955}19561957void Builder::addDecoration(Id id, Decoration decoration, const std::vector<const char*>& strings)1958{1959if (decoration == spv::DecorationMax)1960return;19611962Instruction* dec = new Instruction(OpDecorateString);1963dec->reserveOperands(strings.size() + 2);1964dec->addIdOperand(id);1965dec->addImmediateOperand(decoration);1966for (auto string : strings)1967dec->addStringOperand(string);19681969decorations.push_back(std::unique_ptr<Instruction>(dec));1970}19711972void Builder::addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType) {1973Instruction* dec = new Instruction(OpDecorate);1974dec->reserveOperands(4);1975dec->addIdOperand(id);1976dec->addImmediateOperand(spv::DecorationLinkageAttributes);1977dec->addStringOperand(name);1978dec->addImmediateOperand(linkType);19791980decorations.push_back(std::unique_ptr<Instruction>(dec));1981}19821983void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration)1984{1985if (decoration == spv::DecorationMax)1986return;19871988Instruction* dec = new Instruction(OpDecorateId);1989dec->reserveOperands(3);1990dec->addIdOperand(id);1991dec->addImmediateOperand(decoration);1992dec->addIdOperand(idDecoration);19931994decorations.push_back(std::unique_ptr<Instruction>(dec));1995}19961997void Builder::addDecorationId(Id id, Decoration decoration, const std::vector<Id>& operandIds)1998{1999if(decoration == spv::DecorationMax)2000return;20012002Instruction* dec = new Instruction(OpDecorateId);2003dec->reserveOperands(operandIds.size() + 2);2004dec->addIdOperand(id);2005dec->addImmediateOperand(decoration);20062007for (auto operandId : operandIds)2008dec->addIdOperand(operandId);20092010decorations.push_back(std::unique_ptr<Instruction>(dec));2011}20122013void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num)2014{2015if (decoration == spv::DecorationMax)2016return;20172018Instruction* dec = new Instruction(OpMemberDecorate);2019dec->reserveOperands(3);2020dec->addIdOperand(id);2021dec->addImmediateOperand(member);2022dec->addImmediateOperand(decoration);2023if (num >= 0)2024dec->addImmediateOperand(num);20252026decorations.push_back(std::unique_ptr<Instruction>(dec));2027}20282029void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s)2030{2031if (decoration == spv::DecorationMax)2032return;20332034Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE);2035dec->reserveOperands(4);2036dec->addIdOperand(id);2037dec->addImmediateOperand(member);2038dec->addImmediateOperand(decoration);2039dec->addStringOperand(s);20402041decorations.push_back(std::unique_ptr<Instruction>(dec));2042}20432044void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<unsigned>& literals)2045{2046if (decoration == spv::DecorationMax)2047return;20482049Instruction* dec = new Instruction(OpMemberDecorate);2050dec->reserveOperands(literals.size() + 3);2051dec->addIdOperand(id);2052dec->addImmediateOperand(member);2053dec->addImmediateOperand(decoration);2054for (auto literal : literals)2055dec->addImmediateOperand(literal);20562057decorations.push_back(std::unique_ptr<Instruction>(dec));2058}20592060void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<const char*>& strings)2061{2062if (decoration == spv::DecorationMax)2063return;20642065Instruction* dec = new Instruction(OpMemberDecorateString);2066dec->reserveOperands(strings.size() + 3);2067dec->addIdOperand(id);2068dec->addImmediateOperand(member);2069dec->addImmediateOperand(decoration);2070for (auto string : strings)2071dec->addStringOperand(string);20722073decorations.push_back(std::unique_ptr<Instruction>(dec));2074}20752076void Builder::addInstruction(std::unique_ptr<Instruction> inst) {2077// Optionally insert OpDebugScope2078if (emitNonSemanticShaderDebugInfo && dirtyScopeTracker) {2079if (buildPoint->updateDebugScope(currentDebugScopeId.top())) {2080auto scopeInst = std::make_unique<Instruction>(getUniqueId(), makeVoidType(), OpExtInst);2081scopeInst->reserveOperands(3);2082scopeInst->addIdOperand(nonSemanticShaderDebugInfo);2083scopeInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugScope);2084scopeInst->addIdOperand(currentDebugScopeId.top());2085buildPoint->addInstruction(std::move(scopeInst));2086}20872088dirtyScopeTracker = false;2089}20902091// Insert OpLine/OpDebugLine if the debug source location has changed2092if (trackDebugInfo && dirtyLineTracker) {2093if (buildPoint->updateDebugSourceLocation(currentLine, 0, currentFileId)) {2094if (emitSpirvDebugInfo) {2095auto lineInst = std::make_unique<Instruction>(OpLine);2096lineInst->reserveOperands(3);2097lineInst->addIdOperand(currentFileId);2098lineInst->addImmediateOperand(currentLine);2099lineInst->addImmediateOperand(0);2100buildPoint->addInstruction(std::move(lineInst));2101}2102if (emitNonSemanticShaderDebugInfo) {2103auto lineInst = std::make_unique<Instruction>(getUniqueId(), makeVoidType(), OpExtInst);2104lineInst->reserveOperands(7);2105lineInst->addIdOperand(nonSemanticShaderDebugInfo);2106lineInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLine);2107lineInst->addIdOperand(makeDebugSource(currentFileId));2108lineInst->addIdOperand(makeUintConstant(currentLine));2109lineInst->addIdOperand(makeUintConstant(currentLine));2110lineInst->addIdOperand(makeUintConstant(0));2111lineInst->addIdOperand(makeUintConstant(0));2112buildPoint->addInstruction(std::move(lineInst));2113}2114}21152116dirtyLineTracker = false;2117}21182119buildPoint->addInstruction(std::move(inst));2120}21212122// Comments in header2123Function* Builder::makeEntryPoint(const char* entryPoint)2124{2125assert(! entryPointFunction);21262127auto const returnType = makeVoidType();21282129restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo;2130if(sourceLang == spv::SourceLanguageHLSL) {2131emitNonSemanticShaderDebugInfo = false;2132}21332134Block* entry = nullptr;2135entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, LinkageTypeMax, {}, {}, &entry);21362137emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo;21382139return entryPointFunction;2140}21412142// Comments in header2143Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType,2144const std::vector<Id>& paramTypes,2145const std::vector<std::vector<Decoration>>& decorations, Block** entry)2146{2147// Make the function and initial instructions in it2148Id typeId = makeFunctionType(returnType, paramTypes);2149Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size());2150Id funcId = getUniqueId();2151Function* function = new Function(funcId, returnType, typeId, firstParamId, linkType, name, module);21522153// Set up the precisions2154setPrecision(function->getId(), precision);2155function->setReturnPrecision(precision);2156for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) {2157for (int d = 0; d < (int)decorations[p].size(); ++d) {2158addDecoration(firstParamId + p, decorations[p][d]);2159function->addParamPrecision(p, decorations[p][d]);2160}2161}21622163// reset last debug scope2164if (emitNonSemanticShaderDebugInfo) {2165dirtyScopeTracker = true;2166}21672168// CFG2169assert(entry != nullptr);2170*entry = new Block(getUniqueId(), *function);2171function->addBlock(*entry);2172setBuildPoint(*entry);21732174if (name)2175addName(function->getId(), name);21762177functions.push_back(std::unique_ptr<Function>(function));21782179return function;2180}21812182void Builder::setupDebugFunctionEntry(Function* function, const char* name, int line, const std::vector<Id>& paramTypes,2183const std::vector<char const*>& paramNames)2184{21852186if (!emitNonSemanticShaderDebugInfo)2187return;21882189currentLine = line;2190Id nameId = getStringId(unmangleFunctionName(name));2191Id funcTypeId = function->getFuncTypeId();2192assert(debugId[funcTypeId] != 0);2193Id funcId = function->getId();21942195assert(funcId != 0);21962197// Make the debug function instruction2198Id debugFuncId = makeDebugFunction(function, nameId, funcTypeId);2199debugId[funcId] = debugFuncId;2200currentDebugScopeId.push(debugFuncId);22012202// DebugScope and DebugLine for parameter DebugDeclares2203assert(paramTypes.size() == paramNames.size());2204if ((int)paramTypes.size() > 0) {2205Id firstParamId = function->getParamId(0);22062207for (size_t p = 0; p < paramTypes.size(); ++p) {2208bool passByRef = false;2209Id paramTypeId = paramTypes[p];22102211// For pointer-typed parameters, they are actually passed by reference and we need unwrap the pointer to get the actual parameter type.2212if (isPointerType(paramTypeId) || isArrayType(paramTypeId)) {2213passByRef = true;2214paramTypeId = getContainedTypeId(paramTypeId);2215}22162217auto const& paramName = paramNames[p];2218auto const debugLocalVariableId = createDebugLocalVariable(debugId[paramTypeId], paramName, p + 1);2219auto const paramId = static_cast<Id>(firstParamId + p);2220debugId[paramId] = debugLocalVariableId;22212222if (passByRef) {2223makeDebugDeclare(debugLocalVariableId, paramId);2224} else {2225makeDebugValue(debugLocalVariableId, paramId);2226}2227}2228}22292230// Clear debug scope stack2231if (emitNonSemanticShaderDebugInfo)2232currentDebugScopeId.pop();2233}22342235Id Builder::makeDebugFunction([[maybe_unused]] Function* function, Id nameId, Id funcTypeId)2236{2237assert(function != nullptr);2238assert(nameId != 0);2239assert(funcTypeId != 0);2240assert(debugId[funcTypeId] != 0);22412242Id funcId = getUniqueId();2243auto type = new Instruction(funcId, makeVoidType(), OpExtInst);2244type->reserveOperands(11);2245type->addIdOperand(nonSemanticShaderDebugInfo);2246type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugFunction);2247type->addIdOperand(nameId);2248type->addIdOperand(debugId[funcTypeId]);2249type->addIdOperand(makeDebugSource(currentFileId)); // TODO: This points to file of definition instead of declaration2250type->addIdOperand(makeUintConstant(currentLine)); // TODO: This points to line of definition instead of declaration2251type->addIdOperand(makeUintConstant(0)); // column2252type->addIdOperand(makeDebugCompilationUnit()); // scope2253type->addIdOperand(nameId); // linkage name2254type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic));2255type->addIdOperand(makeUintConstant(currentLine));2256constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));2257module.mapInstruction(type);2258return funcId;2259}22602261Id Builder::makeDebugLexicalBlock(uint32_t line) {2262assert(!currentDebugScopeId.empty());22632264Id lexId = getUniqueId();2265auto lex = new Instruction(lexId, makeVoidType(), OpExtInst);2266lex->reserveOperands(6);2267lex->addIdOperand(nonSemanticShaderDebugInfo);2268lex->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLexicalBlock);2269lex->addIdOperand(makeDebugSource(currentFileId));2270lex->addIdOperand(makeUintConstant(line));2271lex->addIdOperand(makeUintConstant(0)); // column2272lex->addIdOperand(currentDebugScopeId.top()); // scope2273constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(lex));2274module.mapInstruction(lex);2275return lexId;2276}22772278std::string Builder::unmangleFunctionName(std::string const& name) const2279{2280assert(name.length() > 0);22812282if(name.rfind('(') != std::string::npos) {2283return name.substr(0, name.rfind('('));2284} else {2285return name;2286}2287}22882289// Comments in header2290void Builder::makeReturn(bool implicit, Id retVal)2291{2292if (retVal) {2293Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue);2294inst->addIdOperand(retVal);2295addInstruction(std::unique_ptr<Instruction>(inst));2296} else2297addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn)));22982299if (! implicit)2300createAndSetNoPredecessorBlock("post-return");2301}23022303// Comments in header2304void Builder::enterLexicalBlock(uint32_t line)2305{2306// Generate new lexical scope debug instruction2307Id lexId = makeDebugLexicalBlock(line);2308currentDebugScopeId.push(lexId);2309dirtyScopeTracker = true;2310}23112312// Comments in header2313void Builder::leaveLexicalBlock()2314{2315// Pop current scope from stack and clear current scope2316currentDebugScopeId.pop();2317dirtyScopeTracker = true;2318}23192320// Comments in header2321void Builder::enterFunction(Function const* function)2322{2323// Save and disable debugInfo for HLSL entry point function. It is a wrapper2324// function with no user code in it.2325restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo;2326if (sourceLang == spv::SourceLanguageHLSL && function == entryPointFunction) {2327emitNonSemanticShaderDebugInfo = false;2328}23292330if (emitNonSemanticShaderDebugInfo) {2331// Initialize scope state2332Id funcId = function->getFuncId();2333currentDebugScopeId.push(debugId[funcId]);2334// Create DebugFunctionDefinition2335spv::Id resultId = getUniqueId();2336Instruction* defInst = new Instruction(resultId, makeVoidType(), OpExtInst);2337defInst->reserveOperands(4);2338defInst->addIdOperand(nonSemanticShaderDebugInfo);2339defInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugFunctionDefinition);2340defInst->addIdOperand(debugId[funcId]);2341defInst->addIdOperand(funcId);2342addInstruction(std::unique_ptr<Instruction>(defInst));2343}23442345if (auto linkType = function->getLinkType(); linkType != LinkageTypeMax) {2346Id funcId = function->getFuncId();2347addCapability(CapabilityLinkage);2348addLinkageDecoration(funcId, function->getExportName(), linkType);2349}2350}23512352// Comments in header2353void Builder::leaveFunction()2354{2355Block* block = buildPoint;2356Function& function = buildPoint->getParent();2357assert(block);23582359// If our function did not contain a return, add a return void now.2360if (! block->isTerminated()) {2361if (function.getReturnType() == makeVoidType())2362makeReturn(true);2363else {2364makeReturn(true, createUndefined(function.getReturnType()));2365}2366}23672368// Clear function scope from debug scope stack2369if (emitNonSemanticShaderDebugInfo)2370currentDebugScopeId.pop();23712372emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo;2373}23742375// Comments in header2376void Builder::makeStatementTerminator(spv::Op opcode, const char *name)2377{2378addInstruction(std::unique_ptr<Instruction>(new Instruction(opcode)));2379createAndSetNoPredecessorBlock(name);2380}23812382// Comments in header2383void Builder::makeStatementTerminator(spv::Op opcode, const std::vector<Id>& operands, const char* name)2384{2385// It's assumed that the terminator instruction is always of void return type2386// However in future if there is a need for non void return type, new helper2387// methods can be created.2388createNoResultOp(opcode, operands);2389createAndSetNoPredecessorBlock(name);2390}23912392// Comments in header2393Id Builder::createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name, Id initializer,2394bool const compilerGenerated)2395{2396Id pointerType = makePointer(storageClass, type);2397Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable);2398inst->addImmediateOperand(storageClass);2399if (initializer != NoResult)2400inst->addIdOperand(initializer);24012402switch (storageClass) {2403case StorageClassFunction:2404// Validation rules require the declaration in the entry block2405buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst));24062407if (emitNonSemanticShaderDebugInfo && !compilerGenerated)2408{2409auto const debugLocalVariableId = createDebugLocalVariable(debugId[type], name);2410debugId[inst->getResultId()] = debugLocalVariableId;24112412makeDebugDeclare(debugLocalVariableId, inst->getResultId());2413}24142415break;24162417default:2418constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));2419module.mapInstruction(inst);24202421if (emitNonSemanticShaderDebugInfo)2422{2423auto const debugResultId = createDebugGlobalVariable(debugId[type], name, inst->getResultId());2424debugId[inst->getResultId()] = debugResultId;2425}2426break;2427}24282429if (name)2430addName(inst->getResultId(), name);2431setPrecision(inst->getResultId(), precision);24322433return inst->getResultId();2434}24352436// Comments in header2437Id Builder::createUndefined(Id type)2438{2439Instruction* inst = new Instruction(getUniqueId(), type, OpUndef);2440addInstruction(std::unique_ptr<Instruction>(inst));2441return inst->getResultId();2442}24432444// av/vis/nonprivate are unnecessary and illegal for some storage classes.2445spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)2446const2447{2448switch (sc) {2449case spv::StorageClassUniform:2450case spv::StorageClassWorkgroup:2451case spv::StorageClassStorageBuffer:2452case spv::StorageClassPhysicalStorageBufferEXT:2453break;2454default:2455memoryAccess = spv::MemoryAccessMask(memoryAccess &2456~(spv::MemoryAccessMakePointerAvailableKHRMask |2457spv::MemoryAccessMakePointerVisibleKHRMask |2458spv::MemoryAccessNonPrivatePointerKHRMask));2459break;2460}2461return memoryAccess;2462}24632464// Comments in header2465void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope,2466unsigned int alignment)2467{2468Instruction* store = new Instruction(OpStore);2469store->reserveOperands(2);2470store->addIdOperand(lValue);2471store->addIdOperand(rValue);24722473memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));24742475if (memoryAccess != MemoryAccessMaskNone) {2476store->addImmediateOperand(memoryAccess);2477if (memoryAccess & spv::MemoryAccessAlignedMask) {2478store->addImmediateOperand(alignment);2479}2480if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) {2481store->addIdOperand(makeUintConstant(scope));2482}2483}24842485addInstruction(std::unique_ptr<Instruction>(store));2486}24872488// Comments in header2489Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMask memoryAccess,2490spv::Scope scope, unsigned int alignment)2491{2492Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad);2493load->addIdOperand(lValue);24942495memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));24962497if (memoryAccess != MemoryAccessMaskNone) {2498load->addImmediateOperand(memoryAccess);2499if (memoryAccess & spv::MemoryAccessAlignedMask) {2500load->addImmediateOperand(alignment);2501}2502if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) {2503load->addIdOperand(makeUintConstant(scope));2504}2505}25062507addInstruction(std::unique_ptr<Instruction>(load));2508setPrecision(load->getResultId(), precision);25092510return load->getResultId();2511}25122513// Comments in header2514Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets)2515{2516// Figure out the final resulting type.2517Id typeId = getResultingAccessChainType();2518typeId = makePointer(storageClass, typeId);25192520// Make the instruction2521Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain);2522chain->reserveOperands(offsets.size() + 1);2523chain->addIdOperand(base);2524for (int i = 0; i < (int)offsets.size(); ++i)2525chain->addIdOperand(offsets[i]);2526addInstruction(std::unique_ptr<Instruction>(chain));25272528return chain->getResultId();2529}25302531Id Builder::createArrayLength(Id base, unsigned int member)2532{2533spv::Id intType = makeUintType(32);2534Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength);2535length->reserveOperands(2);2536length->addIdOperand(base);2537length->addImmediateOperand(member);2538addInstruction(std::unique_ptr<Instruction>(length));25392540return length->getResultId();2541}25422543Id Builder::createCooperativeMatrixLengthKHR(Id type)2544{2545spv::Id intType = makeUintType(32);25462547// Generate code for spec constants if in spec constant operation2548// generation mode.2549if (generatingOpCodeForSpecConst) {2550return createSpecConstantOp(OpCooperativeMatrixLengthKHR, intType, std::vector<Id>(1, type), std::vector<Id>());2551}25522553Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthKHR);2554length->addIdOperand(type);2555addInstruction(std::unique_ptr<Instruction>(length));25562557return length->getResultId();2558}25592560Id Builder::createCooperativeMatrixLengthNV(Id type)2561{2562spv::Id intType = makeUintType(32);25632564// Generate code for spec constants if in spec constant operation2565// generation mode.2566if (generatingOpCodeForSpecConst) {2567return createSpecConstantOp(OpCooperativeMatrixLengthNV, intType, std::vector<Id>(1, type), std::vector<Id>());2568}25692570Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV);2571length->addIdOperand(type);2572addInstruction(std::unique_ptr<Instruction>(length));25732574return length->getResultId();2575}25762577Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index)2578{2579// Generate code for spec constants if in spec constant operation2580// generation mode.2581if (generatingOpCodeForSpecConst) {2582return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite),2583std::vector<Id>(1, index));2584}2585Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);2586extract->reserveOperands(2);2587extract->addIdOperand(composite);2588extract->addImmediateOperand(index);2589addInstruction(std::unique_ptr<Instruction>(extract));25902591return extract->getResultId();2592}25932594Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes)2595{2596// Generate code for spec constants if in spec constant operation2597// generation mode.2598if (generatingOpCodeForSpecConst) {2599return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes);2600}2601Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);2602extract->reserveOperands(indexes.size() + 1);2603extract->addIdOperand(composite);2604for (int i = 0; i < (int)indexes.size(); ++i)2605extract->addImmediateOperand(indexes[i]);2606addInstruction(std::unique_ptr<Instruction>(extract));26072608return extract->getResultId();2609}26102611Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index)2612{2613Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);2614insert->reserveOperands(3);2615insert->addIdOperand(object);2616insert->addIdOperand(composite);2617insert->addImmediateOperand(index);2618addInstruction(std::unique_ptr<Instruction>(insert));26192620return insert->getResultId();2621}26222623Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes)2624{2625Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);2626insert->reserveOperands(indexes.size() + 2);2627insert->addIdOperand(object);2628insert->addIdOperand(composite);2629for (int i = 0; i < (int)indexes.size(); ++i)2630insert->addImmediateOperand(indexes[i]);2631addInstruction(std::unique_ptr<Instruction>(insert));26322633return insert->getResultId();2634}26352636Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex)2637{2638Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic);2639extract->reserveOperands(2);2640extract->addIdOperand(vector);2641extract->addIdOperand(componentIndex);2642addInstruction(std::unique_ptr<Instruction>(extract));26432644return extract->getResultId();2645}26462647Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex)2648{2649Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic);2650insert->reserveOperands(3);2651insert->addIdOperand(vector);2652insert->addIdOperand(component);2653insert->addIdOperand(componentIndex);2654addInstruction(std::unique_ptr<Instruction>(insert));26552656return insert->getResultId();2657}26582659// An opcode that has no operands, no result id, and no type2660void Builder::createNoResultOp(Op opCode)2661{2662Instruction* op = new Instruction(opCode);2663addInstruction(std::unique_ptr<Instruction>(op));2664}26652666// An opcode that has one id operand, no result id, and no type2667void Builder::createNoResultOp(Op opCode, Id operand)2668{2669Instruction* op = new Instruction(opCode);2670op->addIdOperand(operand);2671addInstruction(std::unique_ptr<Instruction>(op));2672}26732674// An opcode that has one or more operands, no result id, and no type2675void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)2676{2677Instruction* op = new Instruction(opCode);2678op->reserveOperands(operands.size());2679for (auto id : operands) {2680op->addIdOperand(id);2681}2682addInstruction(std::unique_ptr<Instruction>(op));2683}26842685// An opcode that has multiple operands, no result id, and no type2686void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands)2687{2688Instruction* op = new Instruction(opCode);2689op->reserveOperands(operands.size());2690for (auto it = operands.cbegin(); it != operands.cend(); ++it) {2691if (it->isId)2692op->addIdOperand(it->word);2693else2694op->addImmediateOperand(it->word);2695}2696addInstruction(std::unique_ptr<Instruction>(op));2697}26982699void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)2700{2701Instruction* op = new Instruction(OpControlBarrier);2702op->reserveOperands(3);2703op->addIdOperand(makeUintConstant(execution));2704op->addIdOperand(makeUintConstant(memory));2705op->addIdOperand(makeUintConstant(semantics));2706addInstruction(std::unique_ptr<Instruction>(op));2707}27082709void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics)2710{2711Instruction* op = new Instruction(OpMemoryBarrier);2712op->reserveOperands(2);2713op->addIdOperand(makeUintConstant(executionScope));2714op->addIdOperand(makeUintConstant(memorySemantics));2715addInstruction(std::unique_ptr<Instruction>(op));2716}27172718// An opcode that has one operands, a result id, and a type2719Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand)2720{2721// Generate code for spec constants if in spec constant operation2722// generation mode.2723if (generatingOpCodeForSpecConst) {2724return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>());2725}2726Instruction* op = new Instruction(getUniqueId(), typeId, opCode);2727op->addIdOperand(operand);2728addInstruction(std::unique_ptr<Instruction>(op));27292730return op->getResultId();2731}27322733Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right)2734{2735// Generate code for spec constants if in spec constant operation2736// generation mode.2737if (generatingOpCodeForSpecConst) {2738std::vector<Id> operands(2);2739operands[0] = left; operands[1] = right;2740return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>());2741}2742Instruction* op = new Instruction(getUniqueId(), typeId, opCode);2743op->reserveOperands(2);2744op->addIdOperand(left);2745op->addIdOperand(right);2746addInstruction(std::unique_ptr<Instruction>(op));27472748return op->getResultId();2749}27502751Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3)2752{2753// Generate code for spec constants if in spec constant operation2754// generation mode.2755if (generatingOpCodeForSpecConst) {2756std::vector<Id> operands(3);2757operands[0] = op1;2758operands[1] = op2;2759operands[2] = op3;2760return createSpecConstantOp(2761opCode, typeId, operands, std::vector<Id>());2762}2763Instruction* op = new Instruction(getUniqueId(), typeId, opCode);2764op->reserveOperands(3);2765op->addIdOperand(op1);2766op->addIdOperand(op2);2767op->addIdOperand(op3);2768addInstruction(std::unique_ptr<Instruction>(op));27692770return op->getResultId();2771}27722773Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands)2774{2775Instruction* op = new Instruction(getUniqueId(), typeId, opCode);2776op->reserveOperands(operands.size());2777for (auto id : operands)2778op->addIdOperand(id);2779addInstruction(std::unique_ptr<Instruction>(op));27802781return op->getResultId();2782}27832784Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands)2785{2786Instruction* op = new Instruction(getUniqueId(), typeId, opCode);2787op->reserveOperands(operands.size());2788for (auto it = operands.cbegin(); it != operands.cend(); ++it) {2789if (it->isId)2790op->addIdOperand(it->word);2791else2792op->addImmediateOperand(it->word);2793}2794addInstruction(std::unique_ptr<Instruction>(op));27952796return op->getResultId();2797}27982799Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands,2800const std::vector<unsigned>& literals)2801{2802Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp);2803op->reserveOperands(operands.size() + literals.size() + 1);2804op->addImmediateOperand((unsigned) opCode);2805for (auto it = operands.cbegin(); it != operands.cend(); ++it)2806op->addIdOperand(*it);2807for (auto it = literals.cbegin(); it != literals.cend(); ++it)2808op->addImmediateOperand(*it);2809module.mapInstruction(op);2810constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op));28112812// OpSpecConstantOp's using 8 or 16 bit types require the associated capability2813if (containsType(typeId, OpTypeInt, 8))2814addCapability(CapabilityInt8);2815if (containsType(typeId, OpTypeInt, 16))2816addCapability(CapabilityInt16);2817if (containsType(typeId, OpTypeFloat, 16))2818addCapability(CapabilityFloat16);28192820return op->getResultId();2821}28222823Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args)2824{2825Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall);2826op->reserveOperands(args.size() + 1);2827op->addIdOperand(function->getId());2828for (int a = 0; a < (int)args.size(); ++a)2829op->addIdOperand(args[a]);2830addInstruction(std::unique_ptr<Instruction>(op));28312832return op->getResultId();2833}28342835// Comments in header2836Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels)2837{2838if (channels.size() == 1)2839return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision);28402841if (generatingOpCodeForSpecConst) {2842std::vector<Id> operands(2);2843operands[0] = operands[1] = source;2844return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision);2845}2846Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);2847assert(isVector(source));2848swizzle->reserveOperands(channels.size() + 2);2849swizzle->addIdOperand(source);2850swizzle->addIdOperand(source);2851for (int i = 0; i < (int)channels.size(); ++i)2852swizzle->addImmediateOperand(channels[i]);2853addInstruction(std::unique_ptr<Instruction>(swizzle));28542855return setPrecision(swizzle->getResultId(), precision);2856}28572858// Comments in header2859Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels)2860{2861if (channels.size() == 1 && getNumComponents(source) == 1)2862return createCompositeInsert(source, target, typeId, channels.front());28632864Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);28652866assert(isVector(target));2867swizzle->reserveOperands(2);2868swizzle->addIdOperand(target);28692870assert(getNumComponents(source) == (int)channels.size());2871assert(isVector(source));2872swizzle->addIdOperand(source);28732874// Set up an identity shuffle from the base value to the result value2875unsigned int components[4];2876int numTargetComponents = getNumComponents(target);2877for (int i = 0; i < numTargetComponents; ++i)2878components[i] = i;28792880// Punch in the l-value swizzle2881for (int i = 0; i < (int)channels.size(); ++i)2882components[channels[i]] = numTargetComponents + i;28832884// finish the instruction with these components selectors2885swizzle->reserveOperands(numTargetComponents);2886for (int i = 0; i < numTargetComponents; ++i)2887swizzle->addImmediateOperand(components[i]);2888addInstruction(std::unique_ptr<Instruction>(swizzle));28892890return swizzle->getResultId();2891}28922893// Comments in header2894void Builder::promoteScalar(Decoration precision, Id& left, Id& right)2895{2896int direction = getNumComponents(right) - getNumComponents(left);28972898if (direction > 0)2899left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right)));2900else if (direction < 0)2901right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left)));29022903return;2904}29052906// Comments in header2907Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType)2908{2909assert(getNumComponents(scalar) == 1);2910assert(getTypeId(scalar) == getScalarTypeId(vectorType));29112912int numComponents = getNumTypeComponents(vectorType);2913if (numComponents == 1)2914return scalar;29152916Instruction* smear = nullptr;2917if (generatingOpCodeForSpecConst) {2918auto members = std::vector<spv::Id>(numComponents, scalar);2919// Sometime even in spec-constant-op mode, the temporary vector created by2920// promoting a scalar might not be a spec constant. This should depend on2921// the scalar.2922// e.g.:2923// const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar;2924// In such cases, the temporary vector created from a_front_end_const_scalar2925// is not a spec constant vector, even though the binary operation node is marked2926// as 'specConstant' and we are in spec-constant-op mode.2927auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar));2928smear = module.getInstruction(result_id);2929} else {2930smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct);2931smear->reserveOperands(numComponents);2932for (int c = 0; c < numComponents; ++c)2933smear->addIdOperand(scalar);2934addInstruction(std::unique_ptr<Instruction>(smear));2935}29362937return setPrecision(smear->getResultId(), precision);2938}29392940// Comments in header2941Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args)2942{2943Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst);2944inst->reserveOperands(args.size() + 2);2945inst->addIdOperand(builtins);2946inst->addImmediateOperand(entryPoint);2947for (int arg = 0; arg < (int)args.size(); ++arg)2948inst->addIdOperand(args[arg]);29492950addInstruction(std::unique_ptr<Instruction>(inst));29512952return inst->getResultId();2953}29542955// Accept all parameters needed to create a texture instruction.2956// Create the correct instruction based on the inputs, and make the call.2957Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,2958bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask)2959{2960std::vector<Id> texArgs;29612962//2963// Set up the fixed arguments2964//2965bool explicitLod = false;2966texArgs.push_back(parameters.sampler);2967texArgs.push_back(parameters.coords);2968if (parameters.Dref != NoResult)2969texArgs.push_back(parameters.Dref);2970if (parameters.component != NoResult)2971texArgs.push_back(parameters.component);29722973if (parameters.granularity != NoResult)2974texArgs.push_back(parameters.granularity);2975if (parameters.coarse != NoResult)2976texArgs.push_back(parameters.coarse);29772978//2979// Set up the optional arguments2980//2981size_t optArgNum = texArgs.size(); // the position of the mask for the optional arguments, if any.2982ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand2983if (parameters.bias) {2984mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask);2985texArgs.push_back(parameters.bias);2986}2987if (parameters.lod) {2988mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);2989texArgs.push_back(parameters.lod);2990explicitLod = true;2991} else if (parameters.gradX) {2992mask = (ImageOperandsMask)(mask | ImageOperandsGradMask);2993texArgs.push_back(parameters.gradX);2994texArgs.push_back(parameters.gradY);2995explicitLod = true;2996} else if (noImplicitLod && ! fetch && ! gather) {2997// have to explicitly use lod of 0 if not allowed to have them be implicit, and2998// we would otherwise be about to issue an implicit instruction2999mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);3000texArgs.push_back(makeFloatConstant(0.0));3001explicitLod = true;3002}3003if (parameters.offset) {3004if (isConstant(parameters.offset))3005mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask);3006else {3007addCapability(CapabilityImageGatherExtended);3008mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask);3009}3010texArgs.push_back(parameters.offset);3011}3012if (parameters.offsets) {3013addCapability(CapabilityImageGatherExtended);3014mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask);3015texArgs.push_back(parameters.offsets);3016}3017if (parameters.sample) {3018mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask);3019texArgs.push_back(parameters.sample);3020}3021if (parameters.lodClamp) {3022// capability if this bit is used3023addCapability(CapabilityMinLod);30243025mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask);3026texArgs.push_back(parameters.lodClamp);3027}3028if (parameters.nonprivate) {3029mask = mask | ImageOperandsNonPrivateTexelKHRMask;3030}3031if (parameters.volatil) {3032mask = mask | ImageOperandsVolatileTexelKHRMask;3033}3034mask = mask | signExtensionMask;3035// insert the operand for the mask, if any bits were set.3036if (mask != ImageOperandsMaskNone)3037texArgs.insert(texArgs.begin() + optArgNum, mask);30383039//3040// Set up the instruction3041//3042Op opCode = OpNop; // All paths below need to set this3043if (fetch) {3044if (sparse)3045opCode = OpImageSparseFetch;3046else3047opCode = OpImageFetch;3048} else if (parameters.granularity && parameters.coarse) {3049opCode = OpImageSampleFootprintNV;3050} else if (gather) {3051if (parameters.Dref)3052if (sparse)3053opCode = OpImageSparseDrefGather;3054else3055opCode = OpImageDrefGather;3056else3057if (sparse)3058opCode = OpImageSparseGather;3059else3060opCode = OpImageGather;3061} else if (explicitLod) {3062if (parameters.Dref) {3063if (proj)3064if (sparse)3065opCode = OpImageSparseSampleProjDrefExplicitLod;3066else3067opCode = OpImageSampleProjDrefExplicitLod;3068else3069if (sparse)3070opCode = OpImageSparseSampleDrefExplicitLod;3071else3072opCode = OpImageSampleDrefExplicitLod;3073} else {3074if (proj)3075if (sparse)3076opCode = OpImageSparseSampleProjExplicitLod;3077else3078opCode = OpImageSampleProjExplicitLod;3079else3080if (sparse)3081opCode = OpImageSparseSampleExplicitLod;3082else3083opCode = OpImageSampleExplicitLod;3084}3085} else {3086if (parameters.Dref) {3087if (proj)3088if (sparse)3089opCode = OpImageSparseSampleProjDrefImplicitLod;3090else3091opCode = OpImageSampleProjDrefImplicitLod;3092else3093if (sparse)3094opCode = OpImageSparseSampleDrefImplicitLod;3095else3096opCode = OpImageSampleDrefImplicitLod;3097} else {3098if (proj)3099if (sparse)3100opCode = OpImageSparseSampleProjImplicitLod;3101else3102opCode = OpImageSampleProjImplicitLod;3103else3104if (sparse)3105opCode = OpImageSparseSampleImplicitLod;3106else3107opCode = OpImageSampleImplicitLod;3108}3109}31103111// See if the result type is expecting a smeared result.3112// This happens when a legacy shadow*() call is made, which3113// gets a vec4 back instead of a float.3114Id smearedType = resultType;3115if (! isScalarType(resultType)) {3116switch (opCode) {3117case OpImageSampleDrefImplicitLod:3118case OpImageSampleDrefExplicitLod:3119case OpImageSampleProjDrefImplicitLod:3120case OpImageSampleProjDrefExplicitLod:3121resultType = getScalarTypeId(resultType);3122break;3123default:3124break;3125}3126}31273128Id typeId0 = 0;3129Id typeId1 = 0;31303131if (sparse) {3132typeId0 = resultType;3133typeId1 = getDerefTypeId(parameters.texelOut);3134resultType = makeStructResultType(typeId0, typeId1);3135}31363137// Build the SPIR-V instruction3138Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode);3139textureInst->reserveOperands(optArgNum + (texArgs.size() - (optArgNum + 1)));3140for (size_t op = 0; op < optArgNum; ++op)3141textureInst->addIdOperand(texArgs[op]);3142if (optArgNum < texArgs.size())3143textureInst->addImmediateOperand(texArgs[optArgNum]);3144for (size_t op = optArgNum + 1; op < texArgs.size(); ++op)3145textureInst->addIdOperand(texArgs[op]);3146setPrecision(textureInst->getResultId(), precision);3147addInstruction(std::unique_ptr<Instruction>(textureInst));31483149Id resultId = textureInst->getResultId();31503151if (sparse) {3152// set capability3153addCapability(CapabilitySparseResidency);31543155// Decode the return type that was a special structure3156createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut);3157resultId = createCompositeExtract(resultId, typeId0, 0);3158setPrecision(resultId, precision);3159} else {3160// When a smear is needed, do it, as per what was computed3161// above when resultType was changed to a scalar type.3162if (resultType != smearedType)3163resultId = smearScalar(precision, resultId, smearedType);3164}31653166return resultId;3167}31683169// Comments in header3170Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult)3171{3172// Figure out the result type3173Id resultType = 0;3174switch (opCode) {3175case OpImageQuerySize:3176case OpImageQuerySizeLod:3177{3178int numComponents = 0;3179switch (getTypeDimensionality(getImageType(parameters.sampler))) {3180case Dim1D:3181case DimBuffer:3182numComponents = 1;3183break;3184case Dim2D:3185case DimCube:3186case DimRect:3187case DimSubpassData:3188numComponents = 2;3189break;3190case Dim3D:3191numComponents = 3;3192break;31933194default:3195assert(0);3196break;3197}3198if (isArrayedImageType(getImageType(parameters.sampler)))3199++numComponents;32003201Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32);3202if (numComponents == 1)3203resultType = intType;3204else3205resultType = makeVectorType(intType, numComponents);32063207break;3208}3209case OpImageQueryLod:3210resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2);3211break;3212case OpImageQueryLevels:3213case OpImageQuerySamples:3214resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32);3215break;3216default:3217assert(0);3218break;3219}32203221Instruction* query = new Instruction(getUniqueId(), resultType, opCode);3222query->addIdOperand(parameters.sampler);3223if (parameters.coords)3224query->addIdOperand(parameters.coords);3225if (parameters.lod)3226query->addIdOperand(parameters.lod);3227addInstruction(std::unique_ptr<Instruction>(query));3228addCapability(CapabilityImageQuery);32293230return query->getResultId();3231}32323233// External comments in header.3234// Operates recursively to visit the composite's hierarchy.3235Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal)3236{3237Id boolType = makeBoolType();3238Id valueType = getTypeId(value1);32393240Id resultId = NoResult;32413242int numConstituents = getNumTypeConstituents(valueType);32433244// Scalars and Vectors32453246if (isScalarType(valueType) || isVectorType(valueType)) {3247assert(valueType == getTypeId(value2));3248// These just need a single comparison, just have3249// to figure out what it is.3250Op op;3251switch (getMostBasicTypeClass(valueType)) {3252case OpTypeFloat:3253op = equal ? OpFOrdEqual : OpFUnordNotEqual;3254break;3255case OpTypeInt:3256default:3257op = equal ? OpIEqual : OpINotEqual;3258break;3259case OpTypeBool:3260op = equal ? OpLogicalEqual : OpLogicalNotEqual;3261precision = NoPrecision;3262break;3263}32643265if (isScalarType(valueType)) {3266// scalar3267resultId = createBinOp(op, boolType, value1, value2);3268} else {3269// vector3270resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2);3271setPrecision(resultId, precision);3272// reduce vector compares...3273resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId);3274}32753276return setPrecision(resultId, precision);3277}32783279// Only structs, arrays, and matrices should be left.3280// They share in common the reduction operation across their constituents.3281assert(isAggregateType(valueType) || isMatrixType(valueType));32823283// Compare each pair of constituents3284for (int constituent = 0; constituent < numConstituents; ++constituent) {3285std::vector<unsigned> indexes(1, constituent);3286Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent);3287Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent);3288Id constituent1 = createCompositeExtract(value1, constituentType1, indexes);3289Id constituent2 = createCompositeExtract(value2, constituentType2, indexes);32903291Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal);32923293if (constituent == 0)3294resultId = subResultId;3295else3296resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId),3297precision);3298}32993300return resultId;3301}33023303// OpCompositeConstruct3304Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents)3305{3306assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 &&3307getNumTypeConstituents(typeId) == (int)constituents.size()));33083309if (generatingOpCodeForSpecConst) {3310// Sometime, even in spec-constant-op mode, the constant composite to be3311// constructed may not be a specialization constant.3312// e.g.:3313// const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const);3314// The first column vector should be a spec constant one, as a_spec_const is a spec constant.3315// The second column vector should NOT be spec constant, as it does not contain any spec constants.3316// To handle such cases, we check the constituents of the constant vector to determine whether this3317// vector should be created as a spec constant.3318return makeCompositeConstant(typeId, constituents,3319std::any_of(constituents.begin(), constituents.end(),3320[&](spv::Id id) { return isSpecConstant(id); }));3321}33223323Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct);3324op->reserveOperands(constituents.size());3325for (int c = 0; c < (int)constituents.size(); ++c)3326op->addIdOperand(constituents[c]);3327addInstruction(std::unique_ptr<Instruction>(op));33283329return op->getResultId();3330}33313332// Vector or scalar constructor3333Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)3334{3335Id result = NoResult;3336unsigned int numTargetComponents = getNumTypeComponents(resultTypeId);3337unsigned int targetComponent = 0;33383339// Special case: when calling a vector constructor with a single scalar3340// argument, smear the scalar3341if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1)3342return smearScalar(precision, sources[0], resultTypeId);33433344// accumulate the arguments for OpCompositeConstruct3345std::vector<Id> constituents;3346Id scalarTypeId = getScalarTypeId(resultTypeId);33473348// lambda to store the result of visiting an argument component3349const auto latchResult = [&](Id comp) {3350if (numTargetComponents > 1)3351constituents.push_back(comp);3352else3353result = comp;3354++targetComponent;3355};33563357// lambda to visit a vector argument's components3358const auto accumulateVectorConstituents = [&](Id sourceArg) {3359unsigned int sourceSize = getNumComponents(sourceArg);3360unsigned int sourcesToUse = sourceSize;3361if (sourcesToUse + targetComponent > numTargetComponents)3362sourcesToUse = numTargetComponents - targetComponent;33633364for (unsigned int s = 0; s < sourcesToUse; ++s) {3365std::vector<unsigned> swiz;3366swiz.push_back(s);3367latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz));3368}3369};33703371// lambda to visit a matrix argument's components3372const auto accumulateMatrixConstituents = [&](Id sourceArg) {3373unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg);3374unsigned int sourcesToUse = sourceSize;3375if (sourcesToUse + targetComponent > numTargetComponents)3376sourcesToUse = numTargetComponents - targetComponent;33773378int col = 0;3379int row = 0;3380for (unsigned int s = 0; s < sourcesToUse; ++s) {3381if (row >= getNumRows(sourceArg)) {3382row = 0;3383col++;3384}3385std::vector<Id> indexes;3386indexes.push_back(col);3387indexes.push_back(row);3388latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes));3389row++;3390}3391};33923393// Go through the source arguments, each one could have either3394// a single or multiple components to contribute.3395for (unsigned int i = 0; i < sources.size(); ++i) {33963397if (isScalar(sources[i]) || isPointer(sources[i]))3398latchResult(sources[i]);3399else if (isVector(sources[i]))3400accumulateVectorConstituents(sources[i]);3401else if (isMatrix(sources[i]))3402accumulateMatrixConstituents(sources[i]);3403else3404assert(0);34053406if (targetComponent >= numTargetComponents)3407break;3408}34093410// If the result is a vector, make it from the gathered constituents.3411if (constituents.size() > 0) {3412result = createCompositeConstruct(resultTypeId, constituents);3413return setPrecision(result, precision);3414} else {3415// Precision was set when generating this component.3416return result;3417}3418}34193420// Comments in header3421Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)3422{3423Id componentTypeId = getScalarTypeId(resultTypeId);3424int numCols = getTypeNumColumns(resultTypeId);3425int numRows = getTypeNumRows(resultTypeId);34263427Instruction* instr = module.getInstruction(componentTypeId);3428const unsigned bitCount = instr->getImmediateOperand(0);34293430// Optimize matrix constructed from a bigger matrix3431if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) {3432// To truncate the matrix to a smaller number of rows/columns, we need to:3433// 1. For each column, extract the column and truncate it to the required size using shuffle3434// 2. Assemble the resulting matrix from all columns3435Id matrix = sources[0];3436Id columnTypeId = getContainedTypeId(resultTypeId);3437Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix));34383439std::vector<unsigned> channels;3440for (int row = 0; row < numRows; ++row)3441channels.push_back(row);34423443std::vector<Id> matrixColumns;3444for (int col = 0; col < numCols; ++col) {3445std::vector<unsigned> indexes;3446indexes.push_back(col);3447Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes);3448setPrecision(colv, precision);34493450if (numRows != getNumRows(matrix)) {3451matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels));3452} else {3453matrixColumns.push_back(colv);3454}3455}34563457return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);3458}34593460// Otherwise, will use a two step process3461// 1. make a compile-time 2D array of values3462// 2. construct a matrix from that array34633464// Step 1.34653466// initialize the array to the identity matrix3467Id ids[maxMatrixSize][maxMatrixSize];3468Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0));3469Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0));3470for (int col = 0; col < 4; ++col) {3471for (int row = 0; row < 4; ++row) {3472if (col == row)3473ids[col][row] = one;3474else3475ids[col][row] = zero;3476}3477}34783479// modify components as dictated by the arguments3480if (sources.size() == 1 && isScalar(sources[0])) {3481// a single scalar; resets the diagonals3482for (int col = 0; col < 4; ++col)3483ids[col][col] = sources[0];3484} else if (isMatrix(sources[0])) {3485// constructing from another matrix; copy over the parts that exist in both the argument and constructee3486Id matrix = sources[0];3487int minCols = std::min(numCols, getNumColumns(matrix));3488int minRows = std::min(numRows, getNumRows(matrix));3489for (int col = 0; col < minCols; ++col) {3490std::vector<unsigned> indexes;3491indexes.push_back(col);3492for (int row = 0; row < minRows; ++row) {3493indexes.push_back(row);3494ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes);3495indexes.pop_back();3496setPrecision(ids[col][row], precision);3497}3498}3499} else {3500// fill in the matrix in column-major order with whatever argument components are available3501int row = 0;3502int col = 0;35033504for (int arg = 0; arg < (int)sources.size() && col < numCols; ++arg) {3505Id argComp = sources[arg];3506for (int comp = 0; comp < getNumComponents(sources[arg]); ++comp) {3507if (getNumComponents(sources[arg]) > 1) {3508argComp = createCompositeExtract(sources[arg], componentTypeId, comp);3509setPrecision(argComp, precision);3510}3511ids[col][row++] = argComp;3512if (row == numRows) {3513row = 0;3514col++;3515}3516if (col == numCols) {3517// If more components are provided than fit the matrix, discard the rest.3518break;3519}3520}3521}3522}35233524// Step 2: Construct a matrix from that array.3525// First make the column vectors, then make the matrix.35263527// make the column vectors3528Id columnTypeId = getContainedTypeId(resultTypeId);3529std::vector<Id> matrixColumns;3530for (int col = 0; col < numCols; ++col) {3531std::vector<Id> vectorComponents;3532for (int row = 0; row < numRows; ++row)3533vectorComponents.push_back(ids[col][row]);3534Id column = createCompositeConstruct(columnTypeId, vectorComponents);3535setPrecision(column, precision);3536matrixColumns.push_back(column);3537}35383539// make the matrix3540return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);3541}35423543// Comments in header3544Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) :3545builder(gb),3546condition(cond),3547control(ctrl),3548elseBlock(nullptr)3549{3550function = &builder.getBuildPoint()->getParent();35513552// make the blocks, but only put the then-block into the function,3553// the else-block and merge-block will be added later, in order, after3554// earlier code is emitted3555thenBlock = new Block(builder.getUniqueId(), *function);3556mergeBlock = new Block(builder.getUniqueId(), *function);35573558// Save the current block, so that we can add in the flow control split when3559// makeEndIf is called.3560headerBlock = builder.getBuildPoint();35613562function->addBlock(thenBlock);3563builder.setBuildPoint(thenBlock);3564}35653566// Comments in header3567void Builder::If::makeBeginElse()3568{3569// Close out the "then" by having it jump to the mergeBlock3570builder.createBranch(mergeBlock);35713572// Make the first else block and add it to the function3573elseBlock = new Block(builder.getUniqueId(), *function);3574function->addBlock(elseBlock);35753576// Start building the else block3577builder.setBuildPoint(elseBlock);3578}35793580// Comments in header3581void Builder::If::makeEndIf()3582{3583// jump to the merge block3584builder.createBranch(mergeBlock);35853586// Go back to the headerBlock and make the flow control split3587builder.setBuildPoint(headerBlock);3588builder.createSelectionMerge(mergeBlock, control);3589if (elseBlock)3590builder.createConditionalBranch(condition, thenBlock, elseBlock);3591else3592builder.createConditionalBranch(condition, thenBlock, mergeBlock);35933594// add the merge block to the function3595function->addBlock(mergeBlock);3596builder.setBuildPoint(mergeBlock);3597}35983599// Comments in header3600void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues,3601const std::vector<int>& valueIndexToSegment, int defaultSegment,3602std::vector<Block*>& segmentBlocks)3603{3604Function& function = buildPoint->getParent();36053606// make all the blocks3607for (int s = 0; s < numSegments; ++s)3608segmentBlocks.push_back(new Block(getUniqueId(), function));36093610Block* mergeBlock = new Block(getUniqueId(), function);36113612// make and insert the switch's selection-merge instruction3613createSelectionMerge(mergeBlock, control);36143615// make the switch instruction3616Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch);3617switchInst->reserveOperands((caseValues.size() * 2) + 2);3618switchInst->addIdOperand(selector);3619auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock;3620switchInst->addIdOperand(defaultOrMerge->getId());3621defaultOrMerge->addPredecessor(buildPoint);3622for (int i = 0; i < (int)caseValues.size(); ++i) {3623switchInst->addImmediateOperand(caseValues[i]);3624switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId());3625segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint);3626}3627addInstruction(std::unique_ptr<Instruction>(switchInst));36283629// push the merge block3630switchMerges.push(mergeBlock);3631}36323633// Comments in header3634void Builder::addSwitchBreak()3635{3636// branch to the top of the merge block stack3637createBranch(switchMerges.top());3638createAndSetNoPredecessorBlock("post-switch-break");3639}36403641// Comments in header3642void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment)3643{3644int lastSegment = nextSegment - 1;3645if (lastSegment >= 0) {3646// Close out previous segment by jumping, if necessary, to next segment3647if (! buildPoint->isTerminated())3648createBranch(segmentBlock[nextSegment]);3649}3650Block* block = segmentBlock[nextSegment];3651block->getParent().addBlock(block);3652setBuildPoint(block);3653}36543655// Comments in header3656void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/)3657{3658// Close out previous segment by jumping, if necessary, to next segment3659if (! buildPoint->isTerminated())3660addSwitchBreak();36613662switchMerges.top()->getParent().addBlock(switchMerges.top());3663setBuildPoint(switchMerges.top());36643665switchMerges.pop();3666}36673668Block& Builder::makeNewBlock()3669{3670Function& function = buildPoint->getParent();3671auto block = new Block(getUniqueId(), function);3672function.addBlock(block);3673return *block;3674}36753676Builder::LoopBlocks& Builder::makeNewLoop()3677{3678// This verbosity is needed to simultaneously get the same behavior3679// everywhere (id's in the same order), have a syntax that works3680// across lots of versions of C++, have no warnings from pedantic3681// compilation modes, and leave the rest of the code alone.3682Block& head = makeNewBlock();3683Block& body = makeNewBlock();3684Block& merge = makeNewBlock();3685Block& continue_target = makeNewBlock();3686LoopBlocks blocks(head, body, merge, continue_target);3687loops.push(blocks);3688return loops.top();3689}36903691void Builder::createLoopContinue()3692{3693createBranch(&loops.top().continue_target);3694// Set up a block for dead code.3695createAndSetNoPredecessorBlock("post-loop-continue");3696}36973698void Builder::createLoopExit()3699{3700createBranch(&loops.top().merge);3701// Set up a block for dead code.3702createAndSetNoPredecessorBlock("post-loop-break");3703}37043705void Builder::closeLoop()3706{3707loops.pop();3708}37093710void Builder::clearAccessChain()3711{3712accessChain.base = NoResult;3713accessChain.indexChain.clear();3714accessChain.instr = NoResult;3715accessChain.swizzle.clear();3716accessChain.component = NoResult;3717accessChain.preSwizzleBaseType = NoType;3718accessChain.isRValue = false;3719accessChain.coherentFlags.clear();3720accessChain.alignment = 0;3721}37223723// Comments in header3724void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,3725AccessChain::CoherentFlags coherentFlags, unsigned int alignment)3726{3727accessChain.coherentFlags |= coherentFlags;3728accessChain.alignment |= alignment;37293730// swizzles can be stacked in GLSL, but simplified to a single3731// one here; the base type doesn't change3732if (accessChain.preSwizzleBaseType == NoType)3733accessChain.preSwizzleBaseType = preSwizzleBaseType;37343735// if needed, propagate the swizzle for the current access chain3736if (accessChain.swizzle.size() > 0) {3737std::vector<unsigned> oldSwizzle = accessChain.swizzle;3738accessChain.swizzle.resize(0);3739for (unsigned int i = 0; i < swizzle.size(); ++i) {3740assert(swizzle[i] < oldSwizzle.size());3741accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]);3742}3743} else3744accessChain.swizzle = swizzle;37453746// determine if we need to track this swizzle anymore3747simplifyAccessChainSwizzle();3748}37493750// Comments in header3751void Builder::accessChainStore(Id rvalue, Decoration nonUniform, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment)3752{3753assert(accessChain.isRValue == false);37543755transferAccessChainSwizzle(true);37563757// If a swizzle exists and is not full and is not dynamic, then the swizzle will be broken into individual stores.3758if (accessChain.swizzle.size() > 0 &&3759getNumTypeComponents(getResultingAccessChainType()) != (int)accessChain.swizzle.size() &&3760accessChain.component == NoResult) {3761for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {3762accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle[i]));3763accessChain.instr = NoResult;37643765Id base = collapseAccessChain();3766addDecoration(base, nonUniform);37673768accessChain.indexChain.pop_back();3769accessChain.instr = NoResult;37703771// dynamic component should be gone3772assert(accessChain.component == NoResult);37733774Id source = createCompositeExtract(rvalue, getContainedTypeId(getTypeId(rvalue)), i);37753776// take LSB of alignment3777alignment = alignment & ~(alignment & (alignment-1));3778if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) {3779memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);3780}37813782createStore(source, base, memoryAccess, scope, alignment);3783}3784}3785else {3786Id base = collapseAccessChain();3787addDecoration(base, nonUniform);37883789Id source = rvalue;37903791// dynamic component should be gone3792assert(accessChain.component == NoResult);37933794// If swizzle still exists, it may be out-of-order, we must load the target vector,3795// extract and insert elements to perform writeMask and/or swizzle.3796if (accessChain.swizzle.size() > 0) {3797Id tempBaseId = createLoad(base, spv::NoPrecision);3798source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle);3799}38003801// take LSB of alignment3802alignment = alignment & ~(alignment & (alignment-1));3803if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) {3804memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);3805}38063807createStore(source, base, memoryAccess, scope, alignment);3808}3809}38103811// Comments in header3812Id Builder::accessChainLoad(Decoration precision, Decoration l_nonUniform,3813Decoration r_nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess,3814spv::Scope scope, unsigned int alignment)3815{3816Id id;38173818if (accessChain.isRValue) {3819// transfer access chain, but try to stay in registers3820transferAccessChainSwizzle(false);3821if (accessChain.indexChain.size() > 0) {3822Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType;38233824// if all the accesses are constants, we can use OpCompositeExtract3825std::vector<unsigned> indexes;3826bool constant = true;3827for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {3828if (isConstantScalar(accessChain.indexChain[i]))3829indexes.push_back(getConstantScalar(accessChain.indexChain[i]));3830else {3831constant = false;3832break;3833}3834}38353836if (constant) {3837id = createCompositeExtract(accessChain.base, swizzleBase, indexes);3838setPrecision(id, precision);3839} else {3840Id lValue = NoResult;3841if (spvVersion >= Spv_1_4 && isValidInitializer(accessChain.base)) {3842// make a new function variable for this r-value, using an initializer,3843// and mark it as NonWritable so that downstream it can be detected as a lookup3844// table3845lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base),3846"indexable", accessChain.base);3847addDecoration(lValue, DecorationNonWritable);3848} else {3849lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base),3850"indexable");3851// store into it3852createStore(accessChain.base, lValue);3853}3854// move base to the new variable3855accessChain.base = lValue;3856accessChain.isRValue = false;38573858// load through the access chain3859id = createLoad(collapseAccessChain(), precision);3860}3861} else3862id = accessChain.base; // no precision, it was set when this was defined3863} else {3864transferAccessChainSwizzle(true);38653866// take LSB of alignment3867alignment = alignment & ~(alignment & (alignment-1));3868if (getStorageClass(accessChain.base) == StorageClassPhysicalStorageBufferEXT) {3869memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);3870}38713872// load through the access chain3873id = collapseAccessChain();3874// Apply nonuniform both to the access chain and the loaded value.3875// Buffer accesses need the access chain decorated, and this is where3876// loaded image types get decorated. TODO: This should maybe move to3877// createImageTextureFunctionCall.3878addDecoration(id, l_nonUniform);3879id = createLoad(id, precision, memoryAccess, scope, alignment);3880addDecoration(id, r_nonUniform);3881}38823883// Done, unless there are swizzles to do3884if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)3885return id;38863887// Do remaining swizzling38883889// Do the basic swizzle3890if (accessChain.swizzle.size() > 0) {3891Id swizzledType = getScalarTypeId(getTypeId(id));3892if (accessChain.swizzle.size() > 1)3893swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size());3894id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle);3895}38963897// Do the dynamic component3898if (accessChain.component != NoResult)3899id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision);39003901addDecoration(id, r_nonUniform);3902return id;3903}39043905Id Builder::accessChainGetLValue()3906{3907assert(accessChain.isRValue == false);39083909transferAccessChainSwizzle(true);3910Id lvalue = collapseAccessChain();39113912// If swizzle exists, it is out-of-order or not full, we must load the target vector,3913// extract and insert elements to perform writeMask and/or swizzle. This does not3914// go with getting a direct l-value pointer.3915assert(accessChain.swizzle.size() == 0);3916assert(accessChain.component == NoResult);39173918return lvalue;3919}39203921// comment in header3922Id Builder::accessChainGetInferredType()3923{3924// anything to operate on?3925if (accessChain.base == NoResult)3926return NoType;3927Id type = getTypeId(accessChain.base);39283929// do initial dereference3930if (! accessChain.isRValue)3931type = getContainedTypeId(type);39323933// dereference each index3934for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) {3935if (isStructType(type))3936type = getContainedTypeId(type, getConstantScalar(*it));3937else3938type = getContainedTypeId(type);3939}39403941// dereference swizzle3942if (accessChain.swizzle.size() == 1)3943type = getContainedTypeId(type);3944else if (accessChain.swizzle.size() > 1)3945type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size());39463947// dereference component selection3948if (accessChain.component)3949type = getContainedTypeId(type);39503951return type;3952}39533954void Builder::dump(std::vector<unsigned int>& out) const3955{3956// Header, before first instructions:3957out.push_back(MagicNumber);3958out.push_back(spvVersion);3959out.push_back(builderNumber);3960out.push_back(uniqueId + 1);3961out.push_back(0);39623963// Capabilities3964for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) {3965Instruction capInst(0, 0, OpCapability);3966capInst.addImmediateOperand(*it);3967capInst.dump(out);3968}39693970for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) {3971Instruction extInst(0, 0, OpExtension);3972extInst.addStringOperand(it->c_str());3973extInst.dump(out);3974}39753976dumpInstructions(out, imports);3977Instruction memInst(0, 0, OpMemoryModel);3978memInst.addImmediateOperand(addressModel);3979memInst.addImmediateOperand(memoryModel);3980memInst.dump(out);39813982// Instructions saved up while building:3983dumpInstructions(out, entryPoints);3984dumpInstructions(out, executionModes);39853986// Debug instructions3987dumpInstructions(out, strings);3988dumpSourceInstructions(out);3989for (int e = 0; e < (int)sourceExtensions.size(); ++e) {3990Instruction sourceExtInst(0, 0, OpSourceExtension);3991sourceExtInst.addStringOperand(sourceExtensions[e]);3992sourceExtInst.dump(out);3993}3994dumpInstructions(out, names);3995dumpModuleProcesses(out);39963997// Annotation instructions3998dumpInstructions(out, decorations);39994000dumpInstructions(out, constantsTypesGlobals);4001dumpInstructions(out, externals);40024003// The functions4004module.dump(out);4005}40064007//4008// Protected methods.4009//40104011// Turn the described access chain in 'accessChain' into an instruction(s)4012// computing its address. This *cannot* include complex swizzles, which must4013// be handled after this is called.4014//4015// Can generate code.4016Id Builder::collapseAccessChain()4017{4018assert(accessChain.isRValue == false);40194020// did we already emit an access chain for this?4021if (accessChain.instr != NoResult)4022return accessChain.instr;40234024// If we have a dynamic component, we can still transfer4025// that into a final operand to the access chain. We need to remap the4026// dynamic component through the swizzle to get a new dynamic component to4027// update.4028//4029// This was not done in transferAccessChainSwizzle() because it might4030// generate code.4031remapDynamicSwizzle();4032if (accessChain.component != NoResult) {4033// transfer the dynamic component to the access chain4034accessChain.indexChain.push_back(accessChain.component);4035accessChain.component = NoResult;4036}40374038// note that non-trivial swizzling is left pending40394040// do we have an access chain?4041if (accessChain.indexChain.size() == 0)4042return accessChain.base;40434044// emit the access chain4045StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base));4046accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain);40474048return accessChain.instr;4049}40504051// For a dynamic component selection of a swizzle.4052//4053// Turn the swizzle and dynamic component into just a dynamic component.4054//4055// Generates code.4056void Builder::remapDynamicSwizzle()4057{4058// do we have a swizzle to remap a dynamic component through?4059if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) {4060// build a vector of the swizzle for the component to map into4061std::vector<Id> components;4062for (int c = 0; c < (int)accessChain.swizzle.size(); ++c)4063components.push_back(makeUintConstant(accessChain.swizzle[c]));4064Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size());4065Id map = makeCompositeConstant(mapType, components);40664067// use it4068accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component);4069accessChain.swizzle.clear();4070}4071}40724073// clear out swizzle if it is redundant, that is reselecting the same components4074// that would be present without the swizzle.4075void Builder::simplifyAccessChainSwizzle()4076{4077// If the swizzle has fewer components than the vector, it is subsetting, and must stay4078// to preserve that fact.4079if (getNumTypeComponents(accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size())4080return;40814082// if components are out of order, it is a swizzle4083for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {4084if (i != accessChain.swizzle[i])4085return;4086}40874088// otherwise, there is no need to track this swizzle4089accessChain.swizzle.clear();4090if (accessChain.component == NoResult)4091accessChain.preSwizzleBaseType = NoType;4092}40934094// To the extent any swizzling can become part of the chain4095// of accesses instead of a post operation, make it so.4096// If 'dynamic' is true, include transferring the dynamic component,4097// otherwise, leave it pending.4098//4099// Does not generate code. just updates the access chain.4100void Builder::transferAccessChainSwizzle(bool dynamic)4101{4102// non existent?4103if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)4104return;41054106// too complex?4107// (this requires either a swizzle, or generating code for a dynamic component)4108if (accessChain.swizzle.size() > 1)4109return;41104111// single component, either in the swizzle and/or dynamic component4112if (accessChain.swizzle.size() == 1) {4113assert(accessChain.component == NoResult);4114// handle static component selection4115accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front()));4116accessChain.swizzle.clear();4117accessChain.preSwizzleBaseType = NoType;4118} else if (dynamic && accessChain.component != NoResult) {4119assert(accessChain.swizzle.size() == 0);4120// handle dynamic component4121accessChain.indexChain.push_back(accessChain.component);4122accessChain.preSwizzleBaseType = NoType;4123accessChain.component = NoResult;4124}4125}41264127// Utility method for creating a new block and setting the insert point to4128// be in it. This is useful for flow-control operations that need a "dummy"4129// block proceeding them (e.g. instructions after a discard, etc).4130void Builder::createAndSetNoPredecessorBlock(const char* /*name*/)4131{4132Block* block = new Block(getUniqueId(), buildPoint->getParent());4133block->setUnreachable();4134buildPoint->getParent().addBlock(block);4135setBuildPoint(block);41364137// if (name)4138// addName(block->getId(), name);4139}41404141// Comments in header4142void Builder::createBranch(Block* block)4143{4144Instruction* branch = new Instruction(OpBranch);4145branch->addIdOperand(block->getId());4146addInstruction(std::unique_ptr<Instruction>(branch));4147block->addPredecessor(buildPoint);4148}41494150void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control)4151{4152Instruction* merge = new Instruction(OpSelectionMerge);4153merge->reserveOperands(2);4154merge->addIdOperand(mergeBlock->getId());4155merge->addImmediateOperand(control);4156addInstruction(std::unique_ptr<Instruction>(merge));4157}41584159void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,4160const std::vector<unsigned int>& operands)4161{4162Instruction* merge = new Instruction(OpLoopMerge);4163merge->reserveOperands(operands.size() + 3);4164merge->addIdOperand(mergeBlock->getId());4165merge->addIdOperand(continueBlock->getId());4166merge->addImmediateOperand(control);4167for (int op = 0; op < (int)operands.size(); ++op)4168merge->addImmediateOperand(operands[op]);4169addInstruction(std::unique_ptr<Instruction>(merge));4170}41714172void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock)4173{4174Instruction* branch = new Instruction(OpBranchConditional);4175branch->reserveOperands(3);4176branch->addIdOperand(condition);4177branch->addIdOperand(thenBlock->getId());4178branch->addIdOperand(elseBlock->getId());4179addInstruction(std::unique_ptr<Instruction>(branch));4180thenBlock->addPredecessor(buildPoint);4181elseBlock->addPredecessor(buildPoint);4182}41834184// OpSource4185// [OpSourceContinued]4186// ...4187void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text,4188std::vector<unsigned int>& out) const4189{4190const int maxWordCount = 0xFFFF;4191const int opSourceWordCount = 4;4192const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1;41934194if (sourceLang != SourceLanguageUnknown) {4195// OpSource Language Version File Source4196Instruction sourceInst(NoResult, NoType, OpSource);4197sourceInst.reserveOperands(3);4198sourceInst.addImmediateOperand(sourceLang);4199sourceInst.addImmediateOperand(sourceVersion);4200// File operand4201if (fileId != NoResult) {4202sourceInst.addIdOperand(fileId);4203// Source operand4204if (text.size() > 0) {4205int nextByte = 0;4206std::string subString;4207while ((int)text.size() - nextByte > 0) {4208subString = text.substr(nextByte, nonNullBytesPerInstruction);4209if (nextByte == 0) {4210// OpSource4211sourceInst.addStringOperand(subString.c_str());4212sourceInst.dump(out);4213} else {4214// OpSourcContinued4215Instruction sourceContinuedInst(OpSourceContinued);4216sourceContinuedInst.addStringOperand(subString.c_str());4217sourceContinuedInst.dump(out);4218}4219nextByte += nonNullBytesPerInstruction;4220}4221} else4222sourceInst.dump(out);4223} else4224sourceInst.dump(out);4225}4226}42274228// Dump an OpSource[Continued] sequence for the source and every include file4229void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const4230{4231if (emitNonSemanticShaderDebugInfo) return;4232dumpSourceInstructions(mainFileId, sourceText, out);4233for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr)4234dumpSourceInstructions(iItr->first, *iItr->second, out);4235}42364237void Builder::dumpInstructions(std::vector<unsigned int>& out,4238const std::vector<std::unique_ptr<Instruction> >& instructions) const4239{4240for (int i = 0; i < (int)instructions.size(); ++i) {4241instructions[i]->dump(out);4242}4243}42444245void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const4246{4247for (int i = 0; i < (int)moduleProcesses.size(); ++i) {4248Instruction moduleProcessed(OpModuleProcessed);4249moduleProcessed.addStringOperand(moduleProcesses[i]);4250moduleProcessed.dump(out);4251}4252}42534254} // end spv namespace425542564257