blob: d42f728816f1c4d3b87f7b3122ae4528d6896488 [file] [log] [blame]
//
// Copyright (C) 2014-2015 LunarG, Inc.
// Copyright (C) 2015-2018 Google, Inc.
// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Helper for making SPIR-V IR. Generally, this is documented in the header
// SpvBuilder.h.
//
#include <cassert>
#include <cstdlib>
#include <unordered_set>
#include <algorithm>
#include "SpvBuilder.h"
#include "hex_float.h"
#ifndef _WIN32
#include <cstdio>
#endif
namespace spv {
Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) :
spvVersion(spvVersion),
sourceLang(SourceLanguageUnknown),
sourceVersion(0),
sourceFileStringId(NoResult),
currentLine(0),
currentFile(nullptr),
currentFileId(NoResult),
lastDebugScopeId(NoResult),
emitOpLines(false),
emitNonSemanticShaderDebugInfo(false),
addressModel(AddressingModelLogical),
memoryModel(MemoryModelGLSL450),
builderNumber(magicNumber),
buildPoint(nullptr),
uniqueId(0),
entryPointFunction(nullptr),
generatingOpCodeForSpecConst(false),
logger(buildLogger)
{
clearAccessChain();
}
Builder::~Builder()
{
}
Id Builder::import(const char* name)
{
Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport);
import->addStringOperand(name);
module.mapInstruction(import);
imports.push_back(std::unique_ptr<Instruction>(import));
return import->getResultId();
}
// Emit instruction for non-filename-based #line directives (ie. no filename
// seen yet): emit an OpLine if we've been asked to emit OpLines and the line
// number has changed since the last time, and is a valid line number.
void Builder::setLine(int lineNum)
{
if (lineNum != 0 && lineNum != currentLine) {
currentLine = lineNum;
if (emitOpLines) {
if (emitNonSemanticShaderDebugInfo)
addDebugScopeAndLine(currentFileId, currentLine, 0);
else
addLine(sourceFileStringId, currentLine, 0);
}
}
}
// If no filename, do non-filename-based #line emit. Else do filename-based emit.
// Emit OpLine if we've been asked to emit OpLines and the line number or filename
// has changed since the last time, and line number is valid.
void Builder::setLine(int lineNum, const char* filename)
{
if (filename == nullptr) {
setLine(lineNum);
return;
}
if ((lineNum != 0 && lineNum != currentLine) || currentFile == nullptr ||
strncmp(filename, currentFile, strlen(currentFile) + 1) != 0) {
currentLine = lineNum;
currentFile = filename;
if (emitOpLines) {
spv::Id strId = getStringId(filename);
if (emitNonSemanticShaderDebugInfo)
addDebugScopeAndLine(strId, currentLine, 0);
else
addLine(strId, currentLine, 0);
}
}
}
void Builder::addLine(Id fileName, int lineNum, int column)
{
Instruction* line = new Instruction(OpLine);
line->addIdOperand(fileName);
line->addImmediateOperand(lineNum);
line->addImmediateOperand(column);
buildPoint->addInstruction(std::unique_ptr<Instruction>(line));
}
void Builder::addDebugScopeAndLine(Id fileName, int lineNum, int column)
{
assert(!currentDebugScopeId.empty());
if (currentDebugScopeId.top() != lastDebugScopeId) {
spv::Id resultId = getUniqueId();
Instruction* scopeInst = new Instruction(resultId, makeVoidType(), OpExtInst);
scopeInst->addIdOperand(nonSemanticShaderDebugInfo);
scopeInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugScope);
scopeInst->addIdOperand(currentDebugScopeId.top());
buildPoint->addInstruction(std::unique_ptr<Instruction>(scopeInst));
lastDebugScopeId = currentDebugScopeId.top();
}
spv::Id resultId = getUniqueId();
Instruction* lineInst = new Instruction(resultId, makeVoidType(), OpExtInst);
lineInst->addIdOperand(nonSemanticShaderDebugInfo);
lineInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLine);
lineInst->addIdOperand(makeDebugSource(fileName));
lineInst->addIdOperand(makeUintConstant(lineNum));
lineInst->addIdOperand(makeUintConstant(lineNum));
lineInst->addIdOperand(makeUintConstant(column));
lineInst->addIdOperand(makeUintConstant(column));
buildPoint->addInstruction(std::unique_ptr<Instruction>(lineInst));
}
// For creating new groupedTypes (will return old type if the requested one was already made).
Id Builder::makeVoidType()
{
Instruction* type;
if (groupedTypes[OpTypeVoid].size() == 0) {
Id typeId = getUniqueId();
type = new Instruction(typeId, NoType, OpTypeVoid);
groupedTypes[OpTypeVoid].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
// Core OpTypeVoid used for debug void type
if (emitNonSemanticShaderDebugInfo)
debugId[typeId] = typeId;
} else
type = groupedTypes[OpTypeVoid].back();
return type->getResultId();
}
Id Builder::makeBoolType(bool const compilerGenerated)
{
Instruction* type;
if (groupedTypes[OpTypeBool].size() == 0) {
type = new Instruction(getUniqueId(), NoType, OpTypeBool);
groupedTypes[OpTypeBool].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
} else
type = groupedTypes[OpTypeBool].back();
if (emitNonSemanticShaderDebugInfo && !compilerGenerated)
{
auto const debugResultId = makeBoolDebugType(32);
debugId[type->getResultId()] = debugResultId;
}
return type->getResultId();
}
Id Builder::makeSamplerType()
{
Instruction* type;
if (groupedTypes[OpTypeSampler].size() == 0) {
type = new Instruction(getUniqueId(), NoType, OpTypeSampler);
groupedTypes[OpTypeSampler].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
} else
type = groupedTypes[OpTypeSampler].back();
if (emitNonSemanticShaderDebugInfo)
{
auto const debugResultId = makeCompositeDebugType({}, "type.sampler", NonSemanticShaderDebugInfo100Structure, true);
debugId[type->getResultId()] = debugResultId;
}
return type->getResultId();
}
Id Builder::makePointer(StorageClass storageClass, Id pointee)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {
type = groupedTypes[OpTypePointer][t];
if (type->getImmediateOperand(0) == (unsigned)storageClass &&
type->getIdOperand(1) == pointee)
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), NoType, OpTypePointer);
type->addImmediateOperand(storageClass);
type->addIdOperand(pointee);
groupedTypes[OpTypePointer].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeForwardPointer(StorageClass storageClass)
{
// Caching/uniquifying doesn't work here, because we don't know the
// pointee type and there can be multiple forward pointers of the same
// storage type. Somebody higher up in the stack must keep track.
Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer);
type->addImmediateOperand(storageClass);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {
type = groupedTypes[OpTypePointer][t];
if (type->getImmediateOperand(0) == (unsigned)storageClass &&
type->getIdOperand(1) == pointee)
return type->getResultId();
}
type = new Instruction(forwardPointerType, NoType, OpTypePointer);
type->addImmediateOperand(storageClass);
type->addIdOperand(pointee);
groupedTypes[OpTypePointer].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeIntegerType(int width, bool hasSign)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) {
type = groupedTypes[OpTypeInt][t];
if (type->getImmediateOperand(0) == (unsigned)width &&
type->getImmediateOperand(1) == (hasSign ? 1u : 0u))
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), NoType, OpTypeInt);
type->addImmediateOperand(width);
type->addImmediateOperand(hasSign ? 1 : 0);
groupedTypes[OpTypeInt].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
// deal with capabilities
switch (width) {
case 8:
case 16:
// these are currently handled by storage-type declarations and post processing
break;
case 64:
addCapability(CapabilityInt64);
break;
default:
break;
}
if (emitNonSemanticShaderDebugInfo)
{
auto const debugResultId = makeIntegerDebugType(width, hasSign);
debugId[type->getResultId()] = debugResultId;
}
return type->getResultId();
}
Id Builder::makeFloatType(int width)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) {
type = groupedTypes[OpTypeFloat][t];
if (type->getImmediateOperand(0) == (unsigned)width)
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), NoType, OpTypeFloat);
type->addImmediateOperand(width);
groupedTypes[OpTypeFloat].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
// deal with capabilities
switch (width) {
case 16:
// currently handled by storage-type declarations and post processing
break;
case 64:
addCapability(CapabilityFloat64);
break;
default:
break;
}
if (emitNonSemanticShaderDebugInfo)
{
auto const debugResultId = makeFloatDebugType(width);
debugId[type->getResultId()] = debugResultId;
}
return type->getResultId();
}
// Make a struct without checking for duplication.
// See makeStructResultType() for non-decorated structs
// needed as the result of some instructions, which does
// check for duplicates.
Id Builder::makeStructType(const std::vector<Id>& members, const char* name, bool const compilerGenerated)
{
// Don't look for previous one, because in the general case,
// structs can be duplicated except for decorations.
// not found, make it
Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct);
for (int op = 0; op < (int)members.size(); ++op)
type->addIdOperand(members[op]);
groupedTypes[OpTypeStruct].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
addName(type->getResultId(), name);
if (emitNonSemanticShaderDebugInfo && !compilerGenerated)
{
auto const debugResultId = makeCompositeDebugType(members, name, NonSemanticShaderDebugInfo100Structure);
debugId[type->getResultId()] = debugResultId;
}
return type->getResultId();
}
// Make a struct for the simple results of several instructions,
// checking for duplication.
Id Builder::makeStructResultType(Id type0, Id type1)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) {
type = groupedTypes[OpTypeStruct][t];
if (type->getNumOperands() != 2)
continue;
if (type->getIdOperand(0) != type0 ||
type->getIdOperand(1) != type1)
continue;
return type->getResultId();
}
// not found, make it
std::vector<spv::Id> members;
members.push_back(type0);
members.push_back(type1);
return makeStructType(members, "ResType");
}
Id Builder::makeVectorType(Id component, int size)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) {
type = groupedTypes[OpTypeVector][t];
if (type->getIdOperand(0) == component &&
type->getImmediateOperand(1) == (unsigned)size)
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), NoType, OpTypeVector);
type->addIdOperand(component);
type->addImmediateOperand(size);
groupedTypes[OpTypeVector].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
if (emitNonSemanticShaderDebugInfo)
{
auto const debugResultId = makeVectorDebugType(component, size);
debugId[type->getResultId()] = debugResultId;
}
return type->getResultId();
}
Id Builder::makeMatrixType(Id component, int cols, int rows)
{
assert(cols <= maxMatrixSize && rows <= maxMatrixSize);
Id column = makeVectorType(component, rows);
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) {
type = groupedTypes[OpTypeMatrix][t];
if (type->getIdOperand(0) == column &&
type->getImmediateOperand(1) == (unsigned)cols)
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), NoType, OpTypeMatrix);
type->addIdOperand(column);
type->addImmediateOperand(cols);
groupedTypes[OpTypeMatrix].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
if (emitNonSemanticShaderDebugInfo)
{
auto const debugResultId = makeMatrixDebugType(column, cols);
debugId[type->getResultId()] = debugResultId;
}
return type->getResultId();
}
Id Builder::makeCooperativeMatrixTypeKHR(Id component, Id scope, Id rows, Id cols, Id use)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixKHR].size(); ++t) {
type = groupedTypes[OpTypeCooperativeMatrixKHR][t];
if (type->getIdOperand(0) == component &&
type->getIdOperand(1) == scope &&
type->getIdOperand(2) == rows &&
type->getIdOperand(3) == cols &&
type->getIdOperand(4) == use)
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixKHR);
type->addIdOperand(component);
type->addIdOperand(scope);
type->addIdOperand(rows);
type->addIdOperand(cols);
type->addIdOperand(use);
groupedTypes[OpTypeCooperativeMatrixKHR].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeCooperativeMatrixTypeNV(Id component, Id scope, Id rows, Id cols)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixNV].size(); ++t) {
type = groupedTypes[OpTypeCooperativeMatrixNV][t];
if (type->getIdOperand(0) == component && type->getIdOperand(1) == scope && type->getIdOperand(2) == rows &&
type->getIdOperand(3) == cols)
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixNV);
type->addIdOperand(component);
type->addIdOperand(scope);
type->addIdOperand(rows);
type->addIdOperand(cols);
groupedTypes[OpTypeCooperativeMatrixNV].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeCooperativeMatrixTypeWithSameShape(Id component, Id otherType)
{
Instruction* instr = module.getInstruction(otherType);
if (instr->getOpCode() == OpTypeCooperativeMatrixNV) {
return makeCooperativeMatrixTypeNV(component, instr->getIdOperand(1), instr->getIdOperand(2), instr->getIdOperand(3));
} else {
assert(instr->getOpCode() == OpTypeCooperativeMatrixKHR);
return makeCooperativeMatrixTypeKHR(component, instr->getIdOperand(1), instr->getIdOperand(2), instr->getIdOperand(3), instr->getIdOperand(4));
}
}
Id Builder::makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[opcode].size(); ++t) {
type = groupedTypes[opcode][t];
if (static_cast<size_t>(type->getNumOperands()) != operands.size())
continue; // Number mismatch, find next
bool match = true;
for (int op = 0; match && op < (int)operands.size(); ++op) {
match = (operands[op].isId ? type->getIdOperand(op) : type->getImmediateOperand(op)) == operands[op].word;
}
if (match)
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), NoType, opcode);
for (size_t op = 0; op < operands.size(); ++op) {
if (operands[op].isId)
type->addIdOperand(operands[op].word);
else
type->addImmediateOperand(operands[op].word);
}
groupedTypes[opcode].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
// TODO: performance: track arrays per stride
// If a stride is supplied (non-zero) make an array.
// If no stride (0), reuse previous array types.
// 'size' is an Id of a constant or specialization constant of the array size
Id Builder::makeArrayType(Id element, Id sizeId, int stride)
{
Instruction* type;
if (stride == 0) {
// try to find existing type
for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) {
type = groupedTypes[OpTypeArray][t];
if (type->getIdOperand(0) == element &&
type->getIdOperand(1) == sizeId)
return type->getResultId();
}
}
// not found, make it
type = new Instruction(getUniqueId(), NoType, OpTypeArray);
type->addIdOperand(element);
type->addIdOperand(sizeId);
groupedTypes[OpTypeArray].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
if (emitNonSemanticShaderDebugInfo)
{
auto const debugResultId = makeArrayDebugType(element, sizeId);
debugId[type->getResultId()] = debugResultId;
}
return type->getResultId();
}
Id Builder::makeRuntimeArray(Id element)
{
Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray);
type->addIdOperand(element);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
if (emitNonSemanticShaderDebugInfo)
{
auto const debugResultId = makeArrayDebugType(element, makeUintConstant(0));
debugId[type->getResultId()] = debugResultId;
}
return type->getResultId();
}
Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) {
type = groupedTypes[OpTypeFunction][t];
if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1)
continue;
bool mismatch = false;
for (int p = 0; p < (int)paramTypes.size(); ++p) {
if (paramTypes[p] != type->getIdOperand(p + 1)) {
mismatch = true;
break;
}
}
if (! mismatch)
{
// If compiling HLSL, glslang will create a wrapper function around the entrypoint. Accordingly, a void(void)
// function type is created for the wrapper function. However, nonsemantic shader debug information is disabled
// while creating the HLSL wrapper. Consequently, if we encounter another void(void) function, we need to create
// the associated debug function type if it hasn't been created yet.
if(emitNonSemanticShaderDebugInfo && debugId[type->getResultId()] == 0) {
assert(sourceLang == spv::SourceLanguageHLSL);
assert(getTypeClass(returnType) == OpTypeVoid && paramTypes.size() == 0);
Id debugTypeId = makeDebugFunctionType(returnType, {});
debugId[type->getResultId()] = debugTypeId;
}
return type->getResultId();
}
}
// not found, make it
Id typeId = getUniqueId();
type = new Instruction(typeId, NoType, OpTypeFunction);
type->addIdOperand(returnType);
for (int p = 0; p < (int)paramTypes.size(); ++p)
type->addIdOperand(paramTypes[p]);
groupedTypes[OpTypeFunction].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
// make debug type and map it
if (emitNonSemanticShaderDebugInfo) {
Id debugTypeId = makeDebugFunctionType(returnType, paramTypes);
debugId[typeId] = debugTypeId;
}
return type->getResultId();
}
Id Builder::makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes)
{
assert(debugId[returnType] != 0);
Id typeId = getUniqueId();
auto type = new Instruction(typeId, makeVoidType(), OpExtInst);
type->addIdOperand(nonSemanticShaderDebugInfo);
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeFunction);
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic));
type->addIdOperand(debugId[returnType]);
for (auto const paramType : paramTypes) {
if (isPointerType(paramType) || isArrayType(paramType)) {
type->addIdOperand(debugId[getContainedTypeId(paramType)]);
}
else {
type->addIdOperand(debugId[paramType]);
}
}
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return typeId;
}
Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled,
ImageFormat format)
{
assert(sampled == 1 || sampled == 2);
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) {
type = groupedTypes[OpTypeImage][t];
if (type->getIdOperand(0) == sampledType &&
type->getImmediateOperand(1) == (unsigned int)dim &&
type->getImmediateOperand(2) == ( depth ? 1u : 0u) &&
type->getImmediateOperand(3) == (arrayed ? 1u : 0u) &&
type->getImmediateOperand(4) == ( ms ? 1u : 0u) &&
type->getImmediateOperand(5) == sampled &&
type->getImmediateOperand(6) == (unsigned int)format)
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), NoType, OpTypeImage);
type->addIdOperand(sampledType);
type->addImmediateOperand( dim);
type->addImmediateOperand( depth ? 1 : 0);
type->addImmediateOperand(arrayed ? 1 : 0);
type->addImmediateOperand( ms ? 1 : 0);
type->addImmediateOperand(sampled);
type->addImmediateOperand((unsigned int)format);
groupedTypes[OpTypeImage].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
// deal with capabilities
switch (dim) {
case DimBuffer:
if (sampled == 1)
addCapability(CapabilitySampledBuffer);
else
addCapability(CapabilityImageBuffer);
break;
case Dim1D:
if (sampled == 1)
addCapability(CapabilitySampled1D);
else
addCapability(CapabilityImage1D);
break;
case DimCube:
if (arrayed) {
if (sampled == 1)
addCapability(CapabilitySampledCubeArray);
else
addCapability(CapabilityImageCubeArray);
}
break;
case DimRect:
if (sampled == 1)
addCapability(CapabilitySampledRect);
else
addCapability(CapabilityImageRect);
break;
case DimSubpassData:
addCapability(CapabilityInputAttachment);
break;
default:
break;
}
if (ms) {
if (sampled == 2) {
// Images used with subpass data are not storage
// images, so don't require the capability for them.
if (dim != Dim::DimSubpassData)
addCapability(CapabilityStorageImageMultisample);
if (arrayed)
addCapability(CapabilityImageMSArray);
}
}
if (emitNonSemanticShaderDebugInfo)
{
auto TypeName = [&dim]() -> char const* {
switch (dim) {
case Dim1D: return "type.1d.image";
case Dim2D: return "type.2d.image";
case Dim3D: return "type.3d.image";
case DimCube: return "type.cube.image";
default: return "type.image";
}
};
auto const debugResultId = makeCompositeDebugType({}, TypeName(), NonSemanticShaderDebugInfo100Class, true);
debugId[type->getResultId()] = debugResultId;
}
return type->getResultId();
}
Id Builder::makeSampledImageType(Id imageType)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) {
type = groupedTypes[OpTypeSampledImage][t];
if (type->getIdOperand(0) == imageType)
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage);
type->addIdOperand(imageType);
groupedTypes[OpTypeSampledImage].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
if (emitNonSemanticShaderDebugInfo)
{
auto const debugResultId = makeCompositeDebugType({}, "type.sampled.image", NonSemanticShaderDebugInfo100Class, true);
debugId[type->getResultId()] = debugResultId;
}
return type->getResultId();
}
Id Builder::makeDebugInfoNone()
{
if (debugInfoNone != 0)
return debugInfoNone;
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
inst->addIdOperand(nonSemanticShaderDebugInfo);
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugInfoNone);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
module.mapInstruction(inst);
debugInfoNone = inst->getResultId();
return debugInfoNone;
}
Id Builder::makeBoolDebugType(int const size)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) {
type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t];
if (type->getIdOperand(0) == getStringId("bool") &&
type->getIdOperand(1) == static_cast<unsigned int>(size) &&
type->getIdOperand(2) == NonSemanticShaderDebugInfo100Boolean)
return type->getResultId();
}
type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
type->addIdOperand(nonSemanticShaderDebugInfo);
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic);
type->addIdOperand(getStringId("bool")); // name id
type->addIdOperand(makeUintConstant(size)); // size id
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Boolean)); // encoding id
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeIntegerDebugType(int const width, bool const hasSign)
{
const char* typeName = nullptr;
switch (width) {
case 8: typeName = hasSign ? "int8_t" : "uint8_t"; break;
case 16: typeName = hasSign ? "int16_t" : "uint16_t"; break;
case 64: typeName = hasSign ? "int64_t" : "uint64_t"; break;
default: typeName = hasSign ? "int" : "uint";
}
auto nameId = getStringId(typeName);
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) {
type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t];
if (type->getIdOperand(0) == nameId &&
type->getIdOperand(1) == static_cast<unsigned int>(width) &&
type->getIdOperand(2) == (hasSign ? NonSemanticShaderDebugInfo100Signed : NonSemanticShaderDebugInfo100Unsigned))
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
type->addIdOperand(nonSemanticShaderDebugInfo);
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic);
type->addIdOperand(nameId); // name id
type->addIdOperand(makeUintConstant(width)); // size id
if(hasSign == true) {
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Signed)); // encoding id
} else {
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Unsigned)); // encoding id
}
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeFloatDebugType(int const width)
{
const char* typeName = nullptr;
switch (width) {
case 16: typeName = "float16_t"; break;
case 64: typeName = "double"; break;
default: typeName = "float"; break;
}
auto nameId = getStringId(typeName);
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) {
type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t];
if (type->getIdOperand(0) == nameId &&
type->getIdOperand(1) == static_cast<unsigned int>(width) &&
type->getIdOperand(2) == NonSemanticShaderDebugInfo100Float)
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
type->addIdOperand(nonSemanticShaderDebugInfo);
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic);
type->addIdOperand(nameId); // name id
type->addIdOperand(makeUintConstant(width)); // size id
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Float)); // encoding id
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfo100Instructions const sequenceType)
{
assert(sequenceType == NonSemanticShaderDebugInfo100DebugTypeArray ||
sequenceType == NonSemanticShaderDebugInfo100DebugTypeVector);
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedDebugTypes[sequenceType].size(); ++t) {
type = groupedDebugTypes[sequenceType][t];
if (type->getIdOperand(0) == baseType &&
type->getIdOperand(1) == makeUintConstant(componentCount))
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
type->addIdOperand(nonSemanticShaderDebugInfo);
type->addImmediateOperand(sequenceType);
type->addIdOperand(debugId[baseType]); // base type
type->addIdOperand(componentCount); // component count
groupedDebugTypes[sequenceType].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeArrayDebugType(Id const baseType, Id const componentCount)
{
return makeSequentialDebugType(baseType, componentCount, NonSemanticShaderDebugInfo100DebugTypeArray);
}
Id Builder::makeVectorDebugType(Id const baseType, int const componentCount)
{
return makeSequentialDebugType(baseType, makeUintConstant(componentCount), NonSemanticShaderDebugInfo100DebugTypeVector);
}
Id Builder::makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].size(); ++t) {
type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix][t];
if (type->getIdOperand(0) == vectorType &&
type->getIdOperand(1) == makeUintConstant(vectorCount))
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
type->addIdOperand(nonSemanticShaderDebugInfo);
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeMatrix);
type->addIdOperand(debugId[vectorType]); // vector type id
type->addIdOperand(makeUintConstant(vectorCount)); // component count id
type->addIdOperand(makeBoolConstant(columnMajor)); // column-major id
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeMemberDebugType(Id const memberType, DebugTypeLoc const& debugTypeLoc)
{
assert(debugId[memberType] != 0);
Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
type->addIdOperand(nonSemanticShaderDebugInfo);
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeMember);
type->addIdOperand(getStringId(debugTypeLoc.name)); // name id
type->addIdOperand(debugId[memberType]); // type id
type->addIdOperand(makeDebugSource(sourceFileStringId)); // source id TODO: verify this works across include directives
type->addIdOperand(makeUintConstant(debugTypeLoc.line)); // line id TODO: currentLine is always zero
type->addIdOperand(makeUintConstant(debugTypeLoc.column)); // TODO: column id
type->addIdOperand(makeUintConstant(0)); // TODO: offset id
type->addIdOperand(makeUintConstant(0)); // TODO: size id
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMember].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
// Note: To represent a source language opaque type, this instruction must have no Members operands, Size operand must be
// DebugInfoNone, and Name must start with @ to avoid clashes with user defined names.
Id Builder::makeCompositeDebugType(std::vector<Id> const& memberTypes, char const*const name,
NonSemanticShaderDebugInfo100DebugCompositeType const tag, bool const isOpaqueType)
{
// Create the debug member types.
std::vector<Id> memberDebugTypes;
for(auto const memberType : memberTypes) {
assert(debugTypeLocs.find(memberType) != debugTypeLocs.end());
// There _should_ be debug types for all the member types but currently buffer references
// do not have member debug info generated.
if (debugId[memberType])
memberDebugTypes.emplace_back(makeMemberDebugType(memberType, debugTypeLocs[memberType]));
// TODO: Need to rethink this method of passing location information.
// debugTypeLocs.erase(memberType);
}
// Create The structure debug type.
Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
type->addIdOperand(nonSemanticShaderDebugInfo);
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeComposite);
type->addIdOperand(getStringId(name)); // name id
type->addIdOperand(makeUintConstant(tag)); // tag id
type->addIdOperand(makeDebugSource(sourceFileStringId)); // source id TODO: verify this works across include directives
type->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero?
type->addIdOperand(makeUintConstant(0)); // TODO: column id
type->addIdOperand(makeDebugCompilationUnit()); // scope id
if(isOpaqueType == true) {
// Prepend '@' to opaque types.
type->addIdOperand(getStringId('@' + std::string(name))); // linkage name id
type->addIdOperand(makeDebugInfoNone()); // size id
} else {
type->addIdOperand(getStringId(name)); // linkage name id
type->addIdOperand(makeUintConstant(0)); // TODO: size id
}
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id
assert(isOpaqueType == false || (isOpaqueType == true && memberDebugTypes.empty()));
for(auto const memberDebugType : memberDebugTypes) {
type->addIdOperand(memberDebugType);
}
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeComposite].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeDebugSource(const Id fileName) {
if (debugSourceId.find(fileName) != debugSourceId.end())
return debugSourceId[fileName];
spv::Id resultId = getUniqueId();
Instruction* sourceInst = new Instruction(resultId, makeVoidType(), OpExtInst);
sourceInst->addIdOperand(nonSemanticShaderDebugInfo);
sourceInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugSource);
sourceInst->addIdOperand(fileName);
if (emitNonSemanticShaderDebugSource) {
spv::Id sourceId = 0;
if (fileName == sourceFileStringId) {
sourceId = getStringId(sourceText);
} else {
auto incItr = includeFiles.find(fileName);
assert(incItr != includeFiles.end());
sourceId = getStringId(*incItr->second);
}
sourceInst->addIdOperand(sourceId);
}
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceInst));
module.mapInstruction(sourceInst);
debugSourceId[fileName] = resultId;
return resultId;
}
Id Builder::makeDebugCompilationUnit() {
if (nonSemanticShaderCompilationUnitId != 0)
return nonSemanticShaderCompilationUnitId;
spv::Id resultId = getUniqueId();
Instruction* sourceInst = new Instruction(resultId, makeVoidType(), OpExtInst);
sourceInst->addIdOperand(nonSemanticShaderDebugInfo);
sourceInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugCompilationUnit);
sourceInst->addIdOperand(makeUintConstant(1)); // TODO(greg-lunarg): Get rid of magic number
sourceInst->addIdOperand(makeUintConstant(4)); // TODO(greg-lunarg): Get rid of magic number
sourceInst->addIdOperand(makeDebugSource(sourceFileStringId));
sourceInst->addIdOperand(makeUintConstant(sourceLang));
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceInst));
module.mapInstruction(sourceInst);
nonSemanticShaderCompilationUnitId = resultId;
// We can reasonably assume that makeDebugCompilationUnit will be called before any of
// debug-scope stack. Function scopes and lexical scopes will occur afterward.
assert(currentDebugScopeId.empty());
currentDebugScopeId.push(nonSemanticShaderCompilationUnitId);
return resultId;
}
Id Builder::createDebugGlobalVariable(Id const type, char const*const name, Id const variable)
{
assert(type != 0);
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
inst->addIdOperand(nonSemanticShaderDebugInfo);
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugGlobalVariable);
inst->addIdOperand(getStringId(name)); // name id
inst->addIdOperand(type); // type id
inst->addIdOperand(makeDebugSource(sourceFileStringId)); // source id
inst->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero?
inst->addIdOperand(makeUintConstant(0)); // TODO: column id
inst->addIdOperand(makeDebugCompilationUnit()); // scope id
inst->addIdOperand(getStringId(name)); // linkage name id
inst->addIdOperand(variable); // variable id
inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsDefinition)); // flags id
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
module.mapInstruction(inst);
return inst->getResultId();
}
Id Builder::createDebugLocalVariable(Id type, char const*const name, size_t const argNumber)
{
assert(name != nullptr);
assert(!currentDebugScopeId.empty());
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
inst->addIdOperand(nonSemanticShaderDebugInfo);
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLocalVariable);
inst->addIdOperand(getStringId(name)); // name id
inst->addIdOperand(type); // type id
inst->addIdOperand(makeDebugSource(sourceFileStringId)); // source id
inst->addIdOperand(makeUintConstant(currentLine)); // line id
inst->addIdOperand(makeUintConstant(0)); // TODO: column id
inst->addIdOperand(currentDebugScopeId.top()); // scope id
inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsLocal)); // flags id
if(argNumber != 0) {
inst->addIdOperand(makeUintConstant(argNumber));
}
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
module.mapInstruction(inst);
return inst->getResultId();
}
Id Builder::makeDebugExpression()
{
if (debugExpression != 0)
return debugExpression;
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
inst->addIdOperand(nonSemanticShaderDebugInfo);
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugExpression);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
module.mapInstruction(inst);
debugExpression = inst->getResultId();
return debugExpression;
}
Id Builder::makeDebugDeclare(Id const debugLocalVariable, Id const localVariable)
{
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
inst->addIdOperand(nonSemanticShaderDebugInfo);
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugDeclare);
inst->addIdOperand(debugLocalVariable); // debug local variable id
inst->addIdOperand(localVariable); // local variable id
inst->addIdOperand(makeDebugExpression()); // expression id
buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
return inst->getResultId();
}
Id Builder::makeAccelerationStructureType()
{
Instruction *type;
if (groupedTypes[OpTypeAccelerationStructureKHR].size() == 0) {
type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureKHR);
groupedTypes[OpTypeAccelerationStructureKHR].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
} else {
type = groupedTypes[OpTypeAccelerationStructureKHR].back();
}
return type->getResultId();
}
Id Builder::makeRayQueryType()
{
Instruction *type;
if (groupedTypes[OpTypeRayQueryKHR].size() == 0) {
type = new Instruction(getUniqueId(), NoType, OpTypeRayQueryKHR);
groupedTypes[OpTypeRayQueryKHR].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
} else {
type = groupedTypes[OpTypeRayQueryKHR].back();
}
return type->getResultId();
}
Id Builder::makeHitObjectNVType()
{
Instruction *type;
if (groupedTypes[OpTypeHitObjectNV].size() == 0) {
type = new Instruction(getUniqueId(), NoType, OpTypeHitObjectNV);
groupedTypes[OpTypeHitObjectNV].push_back(type);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
} else {
type = groupedTypes[OpTypeHitObjectNV].back();
}
return type->getResultId();
}
Id Builder::getDerefTypeId(Id resultId) const
{
Id typeId = getTypeId(resultId);
assert(isPointerType(typeId));
return module.getInstruction(typeId)->getIdOperand(1);
}
Op Builder::getMostBasicTypeClass(Id typeId) const
{
Instruction* instr = module.getInstruction(typeId);
Op typeClass = instr->getOpCode();
switch (typeClass)
{
case OpTypeVector:
case OpTypeMatrix:
case OpTypeArray:
case OpTypeRuntimeArray:
return getMostBasicTypeClass(instr->getIdOperand(0));
case OpTypePointer:
return getMostBasicTypeClass(instr->getIdOperand(1));
default:
return typeClass;
}
}
int Builder::getNumTypeConstituents(Id typeId) const
{
Instruction* instr = module.getInstruction(typeId);
switch (instr->getOpCode())
{
case OpTypeBool:
case OpTypeInt:
case OpTypeFloat:
case OpTypePointer:
return 1;
case OpTypeVector:
case OpTypeMatrix:
return instr->getImmediateOperand(1);
case OpTypeArray:
{
Id lengthId = instr->getIdOperand(1);
return module.getInstruction(lengthId)->getImmediateOperand(0);
}
case OpTypeStruct:
return instr->getNumOperands();
case OpTypeCooperativeMatrixKHR:
case OpTypeCooperativeMatrixNV:
// has only one constituent when used with OpCompositeConstruct.
return 1;
default:
assert(0);
return 1;
}
}
// Return the lowest-level type of scalar that an homogeneous composite is made out of.
// Typically, this is just to find out if something is made out of ints or floats.
// However, it includes returning a structure, if say, it is an array of structure.
Id Builder::getScalarTypeId(Id typeId) const
{
Instruction* instr = module.getInstruction(typeId);
Op typeClass = instr->getOpCode();
switch (typeClass)
{
case OpTypeVoid:
case OpTypeBool:
case OpTypeInt:
case OpTypeFloat:
case OpTypeStruct:
return instr->getResultId();
case OpTypeVector:
case OpTypeMatrix:
case OpTypeArray:
case OpTypeRuntimeArray:
case OpTypePointer:
return getScalarTypeId(getContainedTypeId(typeId));
default:
assert(0);
return NoResult;
}
}
// Return the type of 'member' of a composite.
Id Builder::getContainedTypeId(Id typeId, int member) const
{
Instruction* instr = module.getInstruction(typeId);
Op typeClass = instr->getOpCode();
switch (typeClass)
{
case OpTypeVector:
case OpTypeMatrix:
case OpTypeArray:
case OpTypeRuntimeArray:
case OpTypeCooperativeMatrixKHR:
case OpTypeCooperativeMatrixNV:
return instr->getIdOperand(0);
case OpTypePointer:
return instr->getIdOperand(1);
case OpTypeStruct:
return instr->getIdOperand(member);
default:
assert(0);
return NoResult;
}
}
// Figure out the final resulting type of the access chain.
Id Builder::getResultingAccessChainType() const
{
assert(accessChain.base != NoResult);
Id typeId = getTypeId(accessChain.base);
assert(isPointerType(typeId));
typeId = getContainedTypeId(typeId);
for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
if (isStructType(typeId)) {
assert(isConstantScalar(accessChain.indexChain[i]));
typeId = getContainedTypeId(typeId, getConstantScalar(accessChain.indexChain[i]));
} else
typeId = getContainedTypeId(typeId, accessChain.indexChain[i]);
}
return typeId;
}
// Return the immediately contained type of a given composite type.
Id Builder::getContainedTypeId(Id typeId) const
{
return getContainedTypeId(typeId, 0);
}
// Returns true if 'typeId' is or contains a scalar type declared with 'typeOp'
// of width 'width'. The 'width' is only consumed for int and float types.
// Returns false otherwise.
bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const
{
const Instruction& instr = *module.getInstruction(typeId);
Op typeClass = instr.getOpCode();
switch (typeClass)
{
case OpTypeInt:
case OpTypeFloat:
return typeClass == typeOp && instr.getImmediateOperand(0) == width;
case OpTypeStruct:
for (int m = 0; m < instr.getNumOperands(); ++m) {
if (containsType(instr.getIdOperand(m), typeOp, width))
return true;
}
return false;
case OpTypePointer:
return false;
case OpTypeVector:
case OpTypeMatrix:
case OpTypeArray:
case OpTypeRuntimeArray:
return containsType(getContainedTypeId(typeId), typeOp, width);
default:
return typeClass == typeOp;
}
}
// return true if the type is a pointer to PhysicalStorageBufferEXT or an
// contains such a pointer. These require restrict/aliased decorations.
bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const
{
const Instruction& instr = *module.getInstruction(typeId);
Op typeClass = instr.getOpCode();
switch (typeClass)
{
case OpTypePointer:
return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT;
case OpTypeArray:
return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId));
case OpTypeStruct:
for (int m = 0; m < instr.getNumOperands(); ++m) {
if (containsPhysicalStorageBufferOrArray(instr.getIdOperand(m)))
return true;
}
return false;
default:
return false;
}
}
// See if a scalar constant of this type has already been created, so it
// can be reused rather than duplicated. (Required by the specification).
Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value)
{
Instruction* constant;
for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
constant = groupedConstants[typeClass][i];
if (constant->getOpCode() == opcode &&
constant->getTypeId() == typeId &&
constant->getImmediateOperand(0) == value)
return constant->getResultId();
}
return 0;
}
// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64').
Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2)
{
Instruction* constant;
for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
constant = groupedConstants[typeClass][i];
if (constant->getOpCode() == opcode &&
constant->getTypeId() == typeId &&
constant->getImmediateOperand(0) == v1 &&
constant->getImmediateOperand(1) == v2)
return constant->getResultId();
}
return 0;
}
// Return true if consuming 'opcode' means consuming a constant.
// "constant" here means after final transform to executable code,
// the value consumed will be a constant, so includes specialization.
bool Builder::isConstantOpCode(Op opcode) const
{
switch (opcode) {
case OpUndef:
case OpConstantTrue:
case OpConstantFalse:
case OpConstant:
case OpConstantComposite:
case OpConstantSampler:
case OpConstantNull:
case OpSpecConstantTrue:
case OpSpecConstantFalse:
case OpSpecConstant:
case OpSpecConstantComposite:
case OpSpecConstantOp:
return true;
default:
return false;
}
}
// Return true if consuming 'opcode' means consuming a specialization constant.
bool Builder::isSpecConstantOpCode(Op opcode) const
{
switch (opcode) {
case OpSpecConstantTrue:
case OpSpecConstantFalse:
case OpSpecConstant:
case OpSpecConstantComposite:
case OpSpecConstantOp:
return true;
default:
return false;
}
}
bool Builder::isRayTracingOpCode(Op opcode) const
{
switch (opcode) {
case OpTypeAccelerationStructureKHR:
case OpTypeRayQueryKHR:
return true;
default:
return false;
}
}
Id Builder::makeNullConstant(Id typeId)
{
Instruction* constant;
// See if we already made it.
Id existing = NoResult;
for (int i = 0; i < (int)nullConstants.size(); ++i) {
constant = nullConstants[i];
if (constant->getTypeId() == typeId)
existing = constant->getResultId();
}
if (existing != NoResult)
return existing;
// Make it
Instruction* c = new Instruction(getUniqueId(), typeId, OpConstantNull);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
nullConstants.push_back(c);
module.mapInstruction(c);
return c->getResultId();
}
Id Builder::makeBoolConstant(bool b, bool specConstant)
{
Id typeId = makeBoolType();
Instruction* constant;
Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse);
// See if we already made it. Applies only to regular constants, because specialization constants
// must remain distinct for the purpose of applying a SpecId decoration.
if (! specConstant) {
Id existing = 0;
for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) {
constant = groupedConstants[OpTypeBool][i];
if (constant->getTypeId() == typeId && constant->getOpCode() == opcode)
existing = constant->getResultId();
}
if (existing)
return existing;
}
// Make it
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
groupedConstants[OpTypeBool].push_back(c);
module.mapInstruction(c);
return c->getResultId();
}
Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant)
{
Op opcode = specConstant ? OpSpecConstant : OpConstant;
// See if we already made it. Applies only to regular constants, because specialization constants
// must remain distinct for the purpose of applying a SpecId decoration.
if (! specConstant) {
Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value);
if (existing)
return existing;
}
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
c->addImmediateOperand(value);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
groupedConstants[OpTypeInt].push_back(c);
module.mapInstruction(c);
return c->getResultId();
}
Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant)
{
Op opcode = specConstant ? OpSpecConstant : OpConstant;
unsigned op1 = value & 0xFFFFFFFF;
unsigned op2 = value >> 32;
// See if we already made it. Applies only to regular constants, because specialization constants
// must remain distinct for the purpose of applying a SpecId decoration.
if (! specConstant) {
Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2);
if (existing)
return existing;
}
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
c->addImmediateOperand(op1);
c->addImmediateOperand(op2);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
groupedConstants[OpTypeInt].push_back(c);
module.mapInstruction(c);
return c->getResultId();
}
Id Builder::makeFloatConstant(float f, bool specConstant)
{
Op opcode = specConstant ? OpSpecConstant : OpConstant;
Id typeId = makeFloatType(32);
union { float fl; unsigned int ui; } u;
u.fl = f;
unsigned value = u.ui;
// See if we already made it. Applies only to regular constants, because specialization constants
// must remain distinct for the purpose of applying a SpecId decoration.
if (! specConstant) {
Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
if (existing)
return existing;
}
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
c->addImmediateOperand(value);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
groupedConstants[OpTypeFloat].push_back(c);
module.mapInstruction(c);
return c->getResultId();
}
Id Builder::makeDoubleConstant(double d, bool specConstant)
{
Op opcode = specConstant ? OpSpecConstant : OpConstant;
Id typeId = makeFloatType(64);
union { double db; unsigned long long ull; } u;
u.db = d;
unsigned long long value = u.ull;
unsigned op1 = value & 0xFFFFFFFF;
unsigned op2 = value >> 32;
// See if we already made it. Applies only to regular constants, because specialization constants
// must remain distinct for the purpose of applying a SpecId decoration.
if (! specConstant) {
Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2);
if (existing)
return existing;
}
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
c->addImmediateOperand(op1);
c->addImmediateOperand(op2);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
groupedConstants[OpTypeFloat].push_back(c);
module.mapInstruction(c);
return c->getResultId();
}
Id Builder::makeFloat16Constant(float f16, bool specConstant)
{
Op opcode = specConstant ? OpSpecConstant : OpConstant;
Id typeId = makeFloatType(16);
spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16);
spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0);
fVal.castTo(f16Val, spvutils::kRoundToZero);
unsigned value = f16Val.value().getAsFloat().get_value();
// See if we already made it. Applies only to regular constants, because specialization constants
// must remain distinct for the purpose of applying a SpecId decoration.
if (!specConstant) {
Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
if (existing)
return existing;
}
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
c->addImmediateOperand(value);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
groupedConstants[OpTypeFloat].push_back(c);
module.mapInstruction(c);
return c->getResultId();
}
Id Builder::makeFpConstant(Id type, double d, bool specConstant)
{
const int width = getScalarTypeWidth(type);
assert(isFloatType(type));
switch (width) {
case 16:
return makeFloat16Constant((float)d, specConstant);
case 32:
return makeFloatConstant((float)d, specConstant);
case 64:
return makeDoubleConstant(d, specConstant);
default:
break;
}
assert(false);
return NoResult;
}
Id Builder::importNonSemanticShaderDebugInfoInstructions()
{
assert(emitNonSemanticShaderDebugInfo == true);
if(nonSemanticShaderDebugInfo == 0)
{
this->addExtension(spv::E_SPV_KHR_non_semantic_info);
nonSemanticShaderDebugInfo = this->import("NonSemantic.Shader.DebugInfo.100");
}
return nonSemanticShaderDebugInfo;
}
Id Builder::findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps)
{
Instruction* constant = nullptr;
bool found = false;
for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
constant = groupedConstants[typeClass][i];
if (constant->getTypeId() != typeId)
continue;
// same contents?
bool mismatch = false;
for (int op = 0; op < constant->getNumOperands(); ++op) {
if (constant->getIdOperand(op) != comps[op]) {
mismatch = true;
break;
}
}
if (! mismatch) {
found = true;
break;
}
}
return found ? constant->getResultId() : NoResult;
}
Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps)
{
Instruction* constant = nullptr;
bool found = false;
for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) {
constant = groupedStructConstants[typeId][i];
// same contents?
bool mismatch = false;
for (int op = 0; op < constant->getNumOperands(); ++op) {
if (constant->getIdOperand(op) != comps[op]) {
mismatch = true;
break;
}
}
if (! mismatch) {
found = true;
break;
}
}
return found ? constant->getResultId() : NoResult;
}
// Comments in header
Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant)
{
Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite;
assert(typeId);
Op typeClass = getTypeClass(typeId);
switch (typeClass) {
case OpTypeVector:
case OpTypeArray:
case OpTypeMatrix:
case OpTypeCooperativeMatrixKHR:
case OpTypeCooperativeMatrixNV:
if (! specConstant) {
Id existing = findCompositeConstant(typeClass, typeId, members);
if (existing)
return existing;
}
break;
case OpTypeStruct:
if (! specConstant) {
Id existing = findStructConstant(typeId, members);
if (existing)
return existing;
}
break;
default:
assert(0);
return makeFloatConstant(0.0);
}
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
for (int op = 0; op < (int)members.size(); ++op)
c->addIdOperand(members[op]);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
if (typeClass == OpTypeStruct)
groupedStructConstants[typeId].push_back(c);
else
groupedConstants[typeClass].push_back(c);
module.mapInstruction(c);
return c->getResultId();
}
Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name)
{
Instruction* entryPoint = new Instruction(OpEntryPoint);
entryPoint->addImmediateOperand(model);
entryPoint->addIdOperand(function->getId());
entryPoint->addStringOperand(name);
entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint));
return entryPoint;
}
// Currently relying on the fact that all 'value' of interest are small non-negative values.
void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3)
{
// entryPoint can be null if we are in compile-only mode
if (!entryPoint)
return;
Instruction* instr = new Instruction(OpExecutionMode);
instr->addIdOperand(entryPoint->getId());
instr->addImmediateOperand(mode);
if (value1 >= 0)
instr->addImmediateOperand(value1);
if (value2 >= 0)
instr->addImmediateOperand(value2);
if (value3 >= 0)
instr->addImmediateOperand(value3);
executionModes.push_back(std::unique_ptr<Instruction>(instr));
}
void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector<unsigned>& literals)
{
// entryPoint can be null if we are in compile-only mode
if (!entryPoint)
return;
Instruction* instr = new Instruction(OpExecutionMode);
instr->addIdOperand(entryPoint->getId());
instr->addImmediateOperand(mode);
for (auto literal : literals)
instr->addImmediateOperand(literal);
executionModes.push_back(std::unique_ptr<Instruction>(instr));
}
void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector<Id>& operandIds)
{
// entryPoint can be null if we are in compile-only mode
if (!entryPoint)
return;
Instruction* instr = new Instruction(OpExecutionModeId);
instr->addIdOperand(entryPoint->getId());
instr->addImmediateOperand(mode);
for (auto operandId : operandIds)
instr->addIdOperand(operandId);
executionModes.push_back(std::unique_ptr<Instruction>(instr));
}
void Builder::addName(Id id, const char* string)
{
Instruction* name = new Instruction(OpName);
name->addIdOperand(id);
name->addStringOperand(string);
names.push_back(std::unique_ptr<Instruction>(name));
}
void Builder::addMemberName(Id id, int memberNumber, const char* string)
{
Instruction* name = new Instruction(OpMemberName);
name->addIdOperand(id);
name->addImmediateOperand(memberNumber);
name->addStringOperand(string);
names.push_back(std::unique_ptr<Instruction>(name));
}
void Builder::addDecoration(Id id, Decoration decoration, int num)
{
if (decoration == spv::DecorationMax)
return;
Instruction* dec = new Instruction(OpDecorate);
dec->addIdOperand(id);
dec->addImmediateOperand(decoration);
if (num >= 0)
dec->addImmediateOperand(num);
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
void Builder::addDecoration(Id id, Decoration decoration, const char* s)
{
if (decoration == spv::DecorationMax)
return;
Instruction* dec = new Instruction(OpDecorateString);
dec->addIdOperand(id);
dec->addImmediateOperand(decoration);
dec->addStringOperand(s);
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
void Builder::addDecoration(Id id, Decoration decoration, const std::vector<unsigned>& literals)
{
if (decoration == spv::DecorationMax)
return;
Instruction* dec = new Instruction(OpDecorate);
dec->addIdOperand(id);
dec->addImmediateOperand(decoration);
for (auto literal : literals)
dec->addImmediateOperand(literal);
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
void Builder::addDecoration(Id id, Decoration decoration, const std::vector<const char*>& strings)
{
if (decoration == spv::DecorationMax)
return;
Instruction* dec = new Instruction(OpDecorateString);
dec->addIdOperand(id);
dec->addImmediateOperand(decoration);
for (auto string : strings)
dec->addStringOperand(string);
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
void Builder::addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType) {
Instruction* dec = new Instruction(OpDecorate);
dec->addIdOperand(id);
dec->addImmediateOperand(spv::DecorationLinkageAttributes);
dec->addStringOperand(name);
dec->addImmediateOperand(linkType);
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration)
{
if (decoration == spv::DecorationMax)
return;
Instruction* dec = new Instruction(OpDecorateId);
dec->addIdOperand(id);
dec->addImmediateOperand(decoration);
dec->addIdOperand(idDecoration);
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
void Builder::addDecorationId(Id id, Decoration decoration, const std::vector<Id>& operandIds)
{
if(decoration == spv::DecorationMax)
return;
Instruction* dec = new Instruction(OpDecorateId);
dec->addIdOperand(id);
dec->addImmediateOperand(decoration);
for (auto operandId : operandIds)
dec->addIdOperand(operandId);
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num)
{
if (decoration == spv::DecorationMax)
return;
Instruction* dec = new Instruction(OpMemberDecorate);
dec->addIdOperand(id);
dec->addImmediateOperand(member);
dec->addImmediateOperand(decoration);
if (num >= 0)
dec->addImmediateOperand(num);
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s)
{
if (decoration == spv::DecorationMax)
return;
Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE);
dec->addIdOperand(id);
dec->addImmediateOperand(member);
dec->addImmediateOperand(decoration);
dec->addStringOperand(s);
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<unsigned>& literals)
{
if (decoration == spv::DecorationMax)
return;
Instruction* dec = new Instruction(OpMemberDecorate);
dec->addIdOperand(id);
dec->addImmediateOperand(member);
dec->addImmediateOperand(decoration);
for (auto literal : literals)
dec->addImmediateOperand(literal);
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<const char*>& strings)
{
if (decoration == spv::DecorationMax)
return;
Instruction* dec = new Instruction(OpMemberDecorateString);
dec->addIdOperand(id);
dec->addImmediateOperand(member);
dec->addImmediateOperand(decoration);
for (auto string : strings)
dec->addStringOperand(string);
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
// Comments in header
Function* Builder::makeEntryPoint(const char* entryPoint)
{
assert(! entryPointFunction);
Block* entry;
std::vector<Id> paramsTypes;
std::vector<char const*> paramNames;
std::vector<std::vector<Decoration>> decorations;
auto const returnType = makeVoidType();
restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo;
if(sourceLang == spv::SourceLanguageHLSL) {
emitNonSemanticShaderDebugInfo = false;
}
entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, LinkageTypeMax, paramsTypes, paramNames, decorations, &entry);
emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo;
return entryPointFunction;
}
// Comments in header
Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType,
const std::vector<Id>& paramTypes, const std::vector<char const*>& paramNames,
const std::vector<std::vector<Decoration>>& decorations, Block **entry)
{
// Make the function and initial instructions in it
Id typeId = makeFunctionType(returnType, paramTypes);
Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size());
Id funcId = getUniqueId();
Function* function = new Function(funcId, returnType, typeId, firstParamId, linkType, name, module);
// Set up the precisions
setPrecision(function->getId(), precision);
function->setReturnPrecision(precision);
for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) {
for (int d = 0; d < (int)decorations[p].size(); ++d) {
addDecoration(firstParamId + p, decorations[p][d]);
function->addParamPrecision(p, decorations[p][d]);
}
}
// Make the debug function instruction
if (emitNonSemanticShaderDebugInfo) {
Id nameId = getStringId(unmangleFunctionName(name));
Id debugFuncId = makeDebugFunction(function, nameId, typeId);
debugId[funcId] = debugFuncId;
currentDebugScopeId.push(debugFuncId);
lastDebugScopeId = NoResult;
}
// CFG
assert(entry != nullptr);
*entry = new Block(getUniqueId(), *function);
function->addBlock(*entry);
setBuildPoint(*entry);
// DebugScope and DebugLine for parameter DebugDeclares
if (emitNonSemanticShaderDebugInfo && (int)paramTypes.size() > 0) {
addDebugScopeAndLine(currentFileId, currentLine, 0);
}
if (emitNonSemanticShaderDebugInfo) {
assert(paramTypes.size() == paramNames.size());
for(size_t p = 0; p < paramTypes.size(); ++p)
{
auto getParamTypeId = [this](Id const& typeId) {
if (isPointerType(typeId) || isArrayType(typeId)) {
return getContainedTypeId(typeId);
}
else {
return typeId;
}
};
auto const& paramName = paramNames[p];
auto const debugLocalVariableId = createDebugLocalVariable(debugId[getParamTypeId(paramTypes[p])], paramName, p+1);
debugId[firstParamId + p] = debugLocalVariableId;
makeDebugDeclare(debugLocalVariableId, firstParamId + p);
}
}
if (name)
addName(function->getId(), name);
functions.push_back(std::unique_ptr<Function>(function));
// Clear debug scope stack
if (emitNonSemanticShaderDebugInfo)
currentDebugScopeId.pop();
return function;
}
Id Builder::makeDebugFunction([[maybe_unused]] Function* function, Id nameId, Id funcTypeId)
{
assert(function != nullptr);
assert(nameId != 0);
assert(funcTypeId != 0);
assert(debugId[funcTypeId] != 0);
Id funcId = getUniqueId();
auto type = new Instruction(funcId, makeVoidType(), OpExtInst);
type->addIdOperand(nonSemanticShaderDebugInfo);
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugFunction);
type->addIdOperand(nameId);
type->addIdOperand(debugId[funcTypeId]);
type->addIdOperand(makeDebugSource(currentFileId)); // Will be fixed later when true filename available
type->addIdOperand(makeUintConstant(currentLine)); // Will be fixed later when true line available
type->addIdOperand(makeUintConstant(0)); // column
type->addIdOperand(makeDebugCompilationUnit()); // scope
type->addIdOperand(nameId); // linkage name
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic));
type->addIdOperand(makeUintConstant(currentLine)); // TODO(greg-lunarg): correct scope line
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
module.mapInstruction(type);
return funcId;
}
Id Builder::makeDebugLexicalBlock(uint32_t line) {
assert(!currentDebugScopeId.empty());
Id lexId = getUniqueId();
auto lex = new Instruction(lexId, makeVoidType(), OpExtInst);
lex->addIdOperand(nonSemanticShaderDebugInfo);
lex->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLexicalBlock);
lex->addIdOperand(makeDebugSource(currentFileId));
lex->addIdOperand(makeUintConstant(line));
lex->addIdOperand(makeUintConstant(0)); // column
lex->addIdOperand(currentDebugScopeId.top()); // scope
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(lex));
module.mapInstruction(lex);
return lexId;
}
std::string Builder::unmangleFunctionName(std::string const& name) const
{
assert(name.length() > 0);
if(name.rfind('(') != std::string::npos) {
return name.substr(0, name.rfind('('));
} else {
return name;
}
}
// Comments in header
void Builder::makeReturn(bool implicit, Id retVal)
{
if (retVal) {
Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue);
inst->addIdOperand(retVal);
buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
} else
buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn)));
if (! implicit)
createAndSetNoPredecessorBlock("post-return");
}
// Comments in header
void Builder::enterScope(uint32_t line)
{
// Generate new lexical scope debug instruction
Id lexId = makeDebugLexicalBlock(line);
currentDebugScopeId.push(lexId);
lastDebugScopeId = NoResult;
}
// Comments in header
void Builder::leaveScope()
{
// Pop current scope from stack and clear current scope
currentDebugScopeId.pop();
lastDebugScopeId = NoResult;
}
// Comments in header
void Builder::enterFunction(Function const* function)
{
// Save and disable debugInfo for HLSL entry point function. It is a wrapper
// function with no user code in it.
restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo;
if (sourceLang == spv::SourceLanguageHLSL && function == entryPointFunction) {
emitNonSemanticShaderDebugInfo = false;
}
if (emitNonSemanticShaderDebugInfo) {
// Initialize scope state
Id funcId = function->getFuncId();
currentDebugScopeId.push(debugId[funcId]);
// Create DebugFunctionDefinition
spv::Id resultId = getUniqueId();
Instruction* defInst = new Instruction(resultId, makeVoidType(), OpExtInst);
defInst->addIdOperand(nonSemanticShaderDebugInfo);
defInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugFunctionDefinition);
defInst->addIdOperand(debugId[funcId]);
defInst->addIdOperand(funcId);
buildPoint->addInstruction(std::unique_ptr<Instruction>(defInst));
}
if (auto linkType = function->getLinkType(); linkType != LinkageTypeMax) {
Id funcId = function->getFuncId();
addCapability(CapabilityLinkage);
addLinkageDecoration(funcId, function->getExportName(), linkType);
}
}
// Comments in header
void Builder::leaveFunction()
{
Block* block = buildPoint;
Function& function = buildPoint->getParent();
assert(block);
// If our function did not contain a return, add a return void now.
if (! block->isTerminated()) {
if (function.getReturnType() == makeVoidType())
makeReturn(true);
else {
makeReturn(true, createUndefined(function.getReturnType()));
}
}
// Clear function scope from debug scope stack
if (emitNonSemanticShaderDebugInfo)
currentDebugScopeId.pop();
emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo;
}
// Comments in header
void Builder::makeStatementTerminator(spv::Op opcode, const char *name)
{
buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(opcode)));
createAndSetNoPredecessorBlock(name);
}
// Comments in header
void Builder::makeStatementTerminator(spv::Op opcode, const std::vector<Id>& operands, const char* name)
{
// It's assumed that the terminator instruction is always of void return type
// However in future if there is a need for non void return type, new helper
// methods can be created.
createNoResultOp(opcode, operands);
createAndSetNoPredecessorBlock(name);
}
// Comments in header
Id Builder::createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name, Id initializer,
bool const compilerGenerated)
{
Id pointerType = makePointer(storageClass, type);
Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable);
inst->addImmediateOperand(storageClass);
if (initializer != NoResult)
inst->addIdOperand(initializer);
switch (storageClass) {
case StorageClassFunction:
// Validation rules require the declaration in the entry block
buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst));
if (emitNonSemanticShaderDebugInfo && !compilerGenerated)
{
auto const debugLocalVariableId = createDebugLocalVariable(debugId[type], name);
debugId[inst->getResultId()] = debugLocalVariableId;
makeDebugDeclare(debugLocalVariableId, inst->getResultId());
}
break;
default:
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
module.mapInstruction(inst);
if (emitNonSemanticShaderDebugInfo && !isRayTracingOpCode(getOpCode(type)))
{
auto const debugResultId = createDebugGlobalVariable(debugId[type], name, inst->getResultId());
debugId[inst->getResultId()] = debugResultId;
}
break;
}
if (name)
addName(inst->getResultId(), name);
setPrecision(inst->getResultId(), precision);
return inst->getResultId();
}
// Comments in header
Id Builder::createUndefined(Id type)
{
Instruction* inst = new Instruction(getUniqueId(), type, OpUndef);
buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
return inst->getResultId();
}
// av/vis/nonprivate are unnecessary and illegal for some storage classes.
spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)
const
{
switch (sc) {
case spv::StorageClassUniform:
case spv::StorageClassWorkgroup:
case spv::StorageClassStorageBuffer:
case spv::StorageClassPhysicalStorageBufferEXT:
break;
default:
memoryAccess = spv::MemoryAccessMask(memoryAccess &
~(spv::MemoryAccessMakePointerAvailableKHRMask |
spv::MemoryAccessMakePointerVisibleKHRMask |
spv::MemoryAccessNonPrivatePointerKHRMask));
break;
}
return memoryAccess;
}
// Comments in header
void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope,
unsigned int alignment)
{
Instruction* store = new Instruction(OpStore);
store->addIdOperand(lValue);
store->addIdOperand(rValue);
memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));
if (memoryAccess != MemoryAccessMaskNone) {
store->addImmediateOperand(memoryAccess);
if (memoryAccess & spv::MemoryAccessAlignedMask) {
store->addImmediateOperand(alignment);
}
if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) {
store->addIdOperand(makeUintConstant(scope));
}
}
buildPoint->addInstruction(std::unique_ptr<Instruction>(store));
}
// Comments in header
Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMask memoryAccess,
spv::Scope scope, unsigned int alignment)
{
Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad);
load->addIdOperand(lValue);
memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));
if (memoryAccess != MemoryAccessMaskNone) {
load->addImmediateOperand(memoryAccess);
if (memoryAccess & spv::MemoryAccessAlignedMask) {
load->addImmediateOperand(alignment);
}
if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) {
load->addIdOperand(makeUintConstant(scope));
}
}
buildPoint->addInstruction(std::unique_ptr<Instruction>(load));
setPrecision(load->getResultId(), precision);
return load->getResultId();
}
// Comments in header
Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets)
{
// Figure out the final resulting type.
Id typeId = getResultingAccessChainType();
typeId = makePointer(storageClass, typeId);
// Make the instruction
Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain);
chain->addIdOperand(base);
for (int i = 0; i < (int)offsets.size(); ++i)
chain->addIdOperand(offsets[i]);
buildPoint->addInstruction(std::unique_ptr<Instruction>(chain));
return chain->getResultId();
}
Id Builder::createArrayLength(Id base, unsigned int member)
{
spv::Id intType = makeUintType(32);
Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength);
length->addIdOperand(base);
length->addImmediateOperand(member);
buildPoint->addInstruction(std::unique_ptr<Instruction>(length));
return length->getResultId();
}
Id Builder::createCooperativeMatrixLengthKHR(Id type)
{
spv::Id intType = makeUintType(32);
// Generate code for spec constants if in spec constant operation
// generation mode.
if (generatingOpCodeForSpecConst) {
return createSpecConstantOp(OpCooperativeMatrixLengthKHR, intType, std::vector<Id>(1, type), std::vector<Id>());
}
Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthKHR);
length->addIdOperand(type);
buildPoint->addInstruction(std::unique_ptr<Instruction>(length));
return length->getResultId();
}
Id Builder::createCooperativeMatrixLengthNV(Id type)
{
spv::Id intType = makeUintType(32);
// Generate code for spec constants if in spec constant operation
// generation mode.
if (generatingOpCodeForSpecConst) {
return createSpecConstantOp(OpCooperativeMatrixLengthNV, intType, std::vector<Id>(1, type), std::vector<Id>());
}
Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV);
length->addIdOperand(type);
buildPoint->addInstruction(std::unique_ptr<Instruction>(length));
return length->getResultId();
}
Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index)
{
// Generate code for spec constants if in spec constant operation
// generation mode.
if (generatingOpCodeForSpecConst) {
return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite),
std::vector<Id>(1, index));
}
Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
extract->addIdOperand(composite);
extract->addImmediateOperand(index);
buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
return extract->getResultId();
}
Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes)
{
// Generate code for spec constants if in spec constant operation
// generation mode.
if (generatingOpCodeForSpecConst) {
return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes);
}
Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
extract->addIdOperand(composite);
for (int i = 0; i < (int)indexes.size(); ++i)
extract->addImmediateOperand(indexes[i]);
buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
return extract->getResultId();
}
Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index)
{
Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
insert->addIdOperand(object);
insert->addIdOperand(composite);
insert->addImmediateOperand(index);
buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
return insert->getResultId();
}
Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes)
{
Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
insert->addIdOperand(object);
insert->addIdOperand(composite);
for (int i = 0; i < (int)indexes.size(); ++i)
insert->addImmediateOperand(indexes[i]);
buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
return insert->getResultId();
}
Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex)
{
Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic);
extract->addIdOperand(vector);
extract->addIdOperand(componentIndex);
buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
return extract->getResultId();
}
Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex)
{
Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic);
insert->addIdOperand(vector);
insert->addIdOperand(component);
insert->addIdOperand(componentIndex);
buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
return insert->getResultId();
}
// An opcode that has no operands, no result id, and no type
void Builder::createNoResultOp(Op opCode)
{
Instruction* op = new Instruction(opCode);
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
}
// An opcode that has one id operand, no result id, and no type
void Builder::createNoResultOp(Op opCode, Id operand)
{
Instruction* op = new Instruction(opCode);
op->addIdOperand(operand);
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
}
// An opcode that has one or more operands, no result id, and no type
void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)
{
Instruction* op = new Instruction(opCode);
for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
op->addIdOperand(*it);
}
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
}
// An opcode that has multiple operands, no result id, and no type
void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands)
{
Instruction* op = new Instruction(opCode);
for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
if (it->isId)
op->addIdOperand(it->word);
else
op->addImmediateOperand(it->word);
}
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
}
void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)
{
Instruction* op = new Instruction(OpControlBarrier);
op->addIdOperand(makeUintConstant(execution));
op->addIdOperand(makeUintConstant(memory));
op->addIdOperand(makeUintConstant(semantics));
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
}
void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics)
{
Instruction* op = new Instruction(OpMemoryBarrier);
op->addIdOperand(makeUintConstant(executionScope));
op->addIdOperand(makeUintConstant(memorySemantics));
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
}
// An opcode that has one operands, a result id, and a type
Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand)
{
// Generate code for spec constants if in spec constant operation
// generation mode.
if (generatingOpCodeForSpecConst) {
return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>());
}
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
op->addIdOperand(operand);
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
return op->getResultId();
}
Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right)
{
// Generate code for spec constants if in spec constant operation
// generation mode.
if (generatingOpCodeForSpecConst) {
std::vector<Id> operands(2);
operands[0] = left; operands[1] = right;
return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>());
}
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
op->addIdOperand(left);
op->addIdOperand(right);
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
return op->getResultId();
}
Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3)
{
// Generate code for spec constants if in spec constant operation
// generation mode.
if (generatingOpCodeForSpecConst) {
std::vector<Id> operands(3);
operands[0] = op1;
operands[1] = op2;
operands[2] = op3;
return createSpecConstantOp(
opCode, typeId, operands, std::vector<Id>());
}
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
op->addIdOperand(op1);
op->addIdOperand(op2);
op->addIdOperand(op3);
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
return op->getResultId();
}
Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands)
{
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
for (auto it = operands.cbegin(); it != operands.cend(); ++it)
op->addIdOperand(*it);
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
return op->getResultId();
}
Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands)
{
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
if (it->isId)
op->addIdOperand(it->word);
else
op->addImmediateOperand(it->word);
}
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
return op->getResultId();
}
Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands,
const std::vector<unsigned>& literals)
{
Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp);
op->addImmediateOperand((unsigned) opCode);
for (auto it = operands.cbegin(); it != operands.cend(); ++it)
op->addIdOperand(*it);
for (auto it = literals.cbegin(); it != literals.cend(); ++it)
op->addImmediateOperand(*it);
module.mapInstruction(op);
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op));
return op->getResultId();
}
Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args)
{
Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall);
op->addIdOperand(function->getId());
for (int a = 0; a < (int)args.size(); ++a)
op->addIdOperand(args[a]);
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
return op->getResultId();
}
// Comments in header
Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels)
{
if (channels.size() == 1)
return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision);
if (generatingOpCodeForSpecConst) {
std::vector<Id> operands(2);
operands[0] = operands[1] = source;
return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision);