Commit 0df0cdee by John Kessenich

glslangValidator: Add straightforward SPIR-V support (non-optimizing, ~3.x functionality).

parent 1899e833
#pragma once
#ifndef Bil_H
#define Bil_H
#endif // Bil_H
//
//Copyright (C) 2014 LunarG, Inc.
//
//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.
#include <assert.h>
#include <stdio.h>
#include "BilBuilder.h"
#ifndef _WIN32
#include <cstdio>
#endif
namespace glbil {
Builder::Builder()
{
}
Builder::~Builder()
{
}
void MissingFunctionality(const char* fun)
{
printf("Missing functionality: %s\n", fun);
}
}; // end glbil namespace
//
//Copyright (C) 2014 LunarG, Inc.
//
//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.
#pragma once
#ifndef BilBuilder_H
#define BilBuilder_H
#include "Bil.h"
#include "Bir.h"
#include <stack>
namespace glbil {
class Builder {
public:
Builder();
virtual ~Builder();
};
void MissingFunctionality(const char*);
}; // end glbil namespace
#endif // BilBuilder_H
//
//Copyright (C) 2014 LunarG, Inc.
//
//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.
#pragma once
#ifndef Bir_H
#define Bir_H
#include "Bil.h"
#include <vector>
#include <iostream>
namespace glbil {
}; // end glbil namespace
#endif // Bir_H
namespace GLSL_STD_450 {
enum Entrypoints {
Round,
Count
};
extern const char* Names[Count];
inline void Initialize()
{
}
}; // end namespace GLSL_STD_450
//
//Copyright (C) 2014 LunarG, Inc.
//
//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.
#include "Bil.h"
#include "GlslangToBil.h"
#include "BilBuilder.h"
// Glslang includes
#include "glslang/MachineIndependent/localintermediate.h"
#include "glslang/MachineIndependent/SymbolTable.h"
namespace glslang {
void GlslangToBil(const glslang::TIntermediate& intermediate, std::vector<unsigned int> bil)
{
}
void OutputBil(const std::vector<unsigned int>& bil, const char* baseName)
{
}
}; // end namespace glslang
...@@ -16,4 +16,4 @@ endif(WIN32) ...@@ -16,4 +16,4 @@ endif(WIN32)
add_subdirectory(glslang) add_subdirectory(glslang)
add_subdirectory(OGLCompilersDLL) add_subdirectory(OGLCompilersDLL)
add_subdirectory(StandAlone) add_subdirectory(StandAlone)
add_subdirectory(BIL) add_subdirectory(SPIRV)
...@@ -3,20 +3,20 @@ cmake_minimum_required(VERSION 2.8) ...@@ -3,20 +3,20 @@ cmake_minimum_required(VERSION 2.8)
include_directories(.. ${CMAKE_CURRENT_BINARY_DIR}) include_directories(.. ${CMAKE_CURRENT_BINARY_DIR})
set(SOURCES set(SOURCES
GlslangToBil.cpp GlslangToSpv.cpp
BilBuilder.cpp) SpvBuilder.cpp)
set(HEADERS set(HEADERS
Bil.h spirv.h
GlslangToBil.h GlslangToSpv.h
BilBuilder.h SpvBuilder.h
Bir.h) SpvIR.h)
add_library(BIL STATIC ${SOURCES} ${HEADERS}) add_library(SPIRV STATIC ${SOURCES} ${HEADERS})
if(WIN32) if(WIN32)
source_group("Source" FILES ${SOURCES} ${HEADERS}) source_group("Source" FILES ${SOURCES} ${HEADERS})
endif(WIN32) endif(WIN32)
install(TARGETS BIL install(TARGETS SPIRV
ARCHIVE DESTINATION lib) ARCHIVE DESTINATION lib)
/*
** Copyright (c) 2014-2015 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
//
// Author: John Kessenich, LunarG
//
namespace GLSL_STD_450 {
enum Entrypoints {
Round,
RoundEven,
Trunc,
Abs,
Sign,
Floor,
Ceil,
Fract,
Radians,
Degrees,
Sin,
Cos,
Tan,
Asin,
Acos,
Atan,
Sinh,
Cosh,
Tanh,
Asinh,
Acosh,
Atanh,
Atan2,
Pow,
Exp,
Log,
Exp2,
Log2,
Sqrt,
InverseSqrt,
Determinant,
MatrixInverse,
Modf, // second argument needs the OpVariable, not an OpLoad
Min,
Max,
Clamp,
Mix,
Step,
SmoothStep,
FloatBitsToInt,
FloatBitsToUint,
IntBitsToFloat,
UintBitsToFloat,
Fma,
Frexp,
Ldexp,
PackSnorm4x8,
PackUnorm4x8,
PackSnorm2x16,
PackUnorm2x16,
PackHalf2x16,
PackDouble2x32,
UnpackSnorm2x16,
UnpackUnorm2x16,
UnpackHalf2x16,
UnpackSnorm4x8,
UnpackUnorm4x8,
UnpackDouble2x32,
Length,
Distance,
Cross,
Normalize,
Ftransform,
FaceForward,
Reflect,
Refract,
UaddCarry,
UsubBorrow,
UmulExtended,
ImulExtended,
BitfieldExtract,
BitfieldInsert,
BitfieldReverse,
BitCount,
FindLSB,
FindMSB,
InterpolateAtCentroid,
InterpolateAtSample,
InterpolateAtOffset,
Count
};
inline void GetDebugNames(const char** names)
{
for (int i = 0; i < Count; ++i)
names[i] = "unknown";
names[Round] = "round";
names[RoundEven] = "roundEven";
names[Trunc] = "trunc";
names[Abs] = "abs";
names[Sign] = "sign";
names[Floor] = "floor";
names[Ceil] = "ceil";
names[Fract] = "fract";
names[Radians] = "radians";
names[Degrees] = "degrees";
names[Sin] = "sin";
names[Cos] = "cos";
names[Tan] = "tan";
names[Asin] = "asin";
names[Acos] = "acos";
names[Atan] = "atan";
names[Sinh] = "sinh";
names[Cosh] = "cosh";
names[Tanh] = "tanh";
names[Asinh] = "asinh";
names[Acosh] = "acosh";
names[Atanh] = "atanh";
names[Atan2] = "atan2";
names[Pow] = "pow";
names[Exp] = "exp";
names[Log] = "log";
names[Exp2] = "exp2";
names[Log2] = "log2";
names[Sqrt] = "sqrt";
names[InverseSqrt] = "inverseSqrt";
names[Determinant] = "determinant";
names[MatrixInverse] = "matrixInverse";
names[Modf] = "modf";
names[Min] = "min";
names[Max] = "max";
names[Clamp] = "clamp";
names[Mix] = "mix";
names[Step] = "step";
names[SmoothStep] = "smoothStep";
names[FloatBitsToInt] = "floatBitsToInt";
names[FloatBitsToUint] = "floatBitsToUint";
names[IntBitsToFloat] = "intBitsToFloat";
names[UintBitsToFloat] = "uintBitsToFloat";
names[Fma] = "fma";
names[Frexp] = "frexp";
names[Ldexp] = "ldexp";
names[PackSnorm4x8] = "packSnorm4x8";
names[PackUnorm4x8] = "packUnorm4x8";
names[PackSnorm2x16] = "packSnorm2x16";
names[PackUnorm2x16] = "packUnorm2x16";
names[PackHalf2x16] = "packHalf2x16";
names[PackDouble2x32] = "packDouble2x32";
names[PackHalf2x16] = "packHalf2x16";
names[UnpackSnorm2x16] = "unpackSnorm2x16";
names[UnpackUnorm2x16] = "unpackUnorm2x16";
names[UnpackHalf2x16] = "unpackHalf2x16";
names[UnpackSnorm4x8] = "unpackSnorm4x8";
names[UnpackUnorm4x8] = "unpackUnorm4x8";
names[UnpackDouble2x32] = "unpackDouble2x32";
names[UnpackHalf2x16] = "unpackHalf2x16";
names[Length] = "length";
names[Distance] = "distance";
names[Cross] = "cross";
names[Normalize] = "normalize";
names[Ftransform] = "ftransform";
names[FaceForward] = "faceForward";
names[Reflect] = "reflect";
names[Refract] = "refract";
names[UaddCarry] = "uaddCarry";
names[UsubBorrow] = "usubBorrow";
names[UmulExtended] = "umulExtended";
names[ImulExtended] = "imulExtended";
names[BitfieldExtract] = "bitfieldExtract";
names[BitfieldInsert] = "bitfieldInsert";
names[BitfieldReverse] = "bitfieldReverse";
names[BitCount] = "bitCount";
names[FindLSB] = "findLSB";
names[FindMSB] = "findMSB";
names[InterpolateAtCentroid] = "interpolateAtCentroid";
names[InterpolateAtSample] = "interpolateAtSample";
names[InterpolateAtOffset] = "interpolateAtOffset";
}
}; // end namespace GLSL_STD_450
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -36,8 +36,7 @@ ...@@ -36,8 +36,7 @@
namespace glslang { namespace glslang {
void GlslangToBil(const glslang::TIntermediate& intermediate, std::vector<unsigned int> bil); void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv);
void OutputSpv(const std::vector<unsigned int>& spirv, const char* baseName);
void OutputBil(const std::vector<unsigned int>& bil, const char* baseName);
}; };
//
//Copyright (C) 2014 LunarG, Inc.
//
//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.
//
// Author: John Kessenich, LunarG
//
//
// Helper for making SPIR-V IR. Generally, this is documented in the header
// SpvBuilder.h.
//
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "SpvBuilder.h"
#ifndef _WIN32
#include <cstdio>
#endif
namespace spv {
const int SpvBuilderMagic = 0xBB;
Builder::Builder(unsigned int userNumber) :
source(LangUnknown),
sourceVersion(0),
addressModel(AddressingLogical),
memoryModel(MemoryGLSL450),
builderNumber(userNumber << 16 | SpvBuilderMagic),
buildPoint(0),
uniqueId(0),
mainFunction(0),
stageExit(0)
{
clearAccessChain();
}
Builder::~Builder()
{
}
Id Builder::import(const char* name)
{
Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport);
import->addStringOperand(name);
imports.push_back(import);
return import->getResultId();
}
// 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) {
type = new Instruction(getUniqueId(), NoType, OpTypeVoid);
groupedTypes[OpTypeVoid].push_back(type);
constantsTypesGlobals.push_back(type);
module.mapInstruction(type);
} else
type = groupedTypes[OpTypeVoid].back();
return type->getResultId();
}
Id Builder::makeBoolType()
{
Instruction* type;
if (groupedTypes[OpTypeBool].size() == 0) {
type = new Instruction(getUniqueId(), NoType, OpTypeBool);
groupedTypes[OpTypeBool].push_back(type);
constantsTypesGlobals.push_back(type);
module.mapInstruction(type);
} else
type = groupedTypes[OpTypeBool].back();
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) == 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(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) == width &&
type->getImmediateOperand(1) == (hasSign ? 1 : 0))
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(type);
module.mapInstruction(type);
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) == 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(type);
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeStructType(std::vector<Id>& members, const char* name)
{
// 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(type);
module.mapInstruction(type);
addName(type->getResultId(), name);
return type->getResultId();
}
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) == 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(type);
module.mapInstruction(type);
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) == 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(type);
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeArrayType(Id element, unsigned size)
{
// First, we need a constant instruction for the size
Id sizeId = makeUintConstant(size);
// try to find existing type
Instruction* 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(type);
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeFunctionType(Id returnType, 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 || 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)
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), 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(type);
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeSampler(Id sampledType, Dimensionality dim, samplerContent content, bool arrayed, bool shadow, bool ms)
{
// try to find it
Instruction* type;
for (int t = 0; t < (int)groupedTypes[OpTypeSampler].size(); ++t) {
type = groupedTypes[OpTypeSampler][t];
if (type->getIdOperand(0) == sampledType &&
type->getImmediateOperand(1) == (unsigned int)dim &&
type->getImmediateOperand(2) == content &&
type->getImmediateOperand(3) == (arrayed ? 1u : 0u) &&
type->getImmediateOperand(4) == ( shadow ? 1u : 0u) &&
type->getImmediateOperand(5) == ( ms ? 1u : 0u))
return type->getResultId();
}
// not found, make it
type = new Instruction(getUniqueId(), NoType, OpTypeSampler);
type->addIdOperand(sampledType);
type->addImmediateOperand( dim);
type->addImmediateOperand(content);
type->addImmediateOperand(arrayed ? 1 : 0);
type->addImmediateOperand( shadow ? 1 : 0);
type->addImmediateOperand( ms ? 1 : 0);
groupedTypes[OpTypeSampler].push_back(type);
constantsTypesGlobals.push_back(type);
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::getDerefTypeId(Id resultId) const
{
Id typeId = getTypeId(resultId);
assert(isPointerType(typeId));
return module.getInstruction(typeId)->getImmediateOperand(1);
}
OpCode Builder::getMostBasicTypeClass(Id typeId) const
{
Instruction* instr = module.getInstruction(typeId);
OpCode typeClass = instr->getOpCode();
switch (typeClass)
{
case OpTypeVoid:
case OpTypeBool:
case OpTypeInt:
case OpTypeFloat:
case OpTypeStruct:
return typeClass;
case OpTypeVector:
case OpTypeMatrix:
case OpTypeArray:
case OpTypeRuntimeArray:
return getMostBasicTypeClass(instr->getIdOperand(0));
case OpTypePointer:
return getMostBasicTypeClass(instr->getIdOperand(1));
default:
MissingFunctionality("getMostBasicTypeClass");
return OpTypeFloat;
}
}
int Builder::getNumTypeComponents(Id typeId) const
{
Instruction* instr = module.getInstruction(typeId);
switch (instr->getOpCode())
{
case OpTypeBool:
case OpTypeInt:
case OpTypeFloat:
return 1;
case OpTypeVector:
case OpTypeMatrix:
return instr->getImmediateOperand(1);
default:
MissingFunctionality("getNumTypeComponents on non bool/int/float/vector/matrix");
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);
OpCode 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:
MissingFunctionality("getScalarTypeId");
return NoResult;
}
}
// Return the type of 'member' of a composite.
Id Builder::getContainedTypeId(Id typeId, int member) const
{
Instruction* instr = module.getInstruction(typeId);
OpCode typeClass = instr->getOpCode();
switch (typeClass)
{
case OpTypeVector:
case OpTypeMatrix:
case OpTypeArray:
case OpTypeRuntimeArray:
return instr->getIdOperand(0);
case OpTypePointer:
return instr->getIdOperand(1);
case OpTypeStruct:
return instr->getIdOperand(member);
default:
MissingFunctionality("getContainedTypeId");
return NoResult;
}
}
// Return the immediately contained type of a given composite type.
Id Builder::getContainedTypeId(Id typeId) const
{
return getContainedTypeId(typeId, 0);
}
// 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(OpCode typeClass, Id typeId, unsigned value) const
{
Instruction* constant;
for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
constant = groupedConstants[typeClass][i];
if (constant->getTypeId() == typeId &&
constant->getImmediateOperand(0) == value)
return constant->getResultId();
}
return 0;
}
Id Builder::makeBoolConstant(bool b)
{
Id typeId = makeBoolType();
Instruction* constant;
// See if we already made it
Id existing = 0;
for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) {
constant = groupedConstants[OpTypeBool][i];
if (constant->getTypeId() == typeId &&
(b ? (constant->getOpCode() == OpConstantTrue) :
(constant->getOpCode() == OpConstantFalse)))
existing = constant->getResultId();
}
if (existing)
return existing;
// Make it
Instruction* c = new Instruction(getUniqueId(), typeId, b ? OpConstantTrue : OpConstantFalse);
constantsTypesGlobals.push_back(c);
groupedConstants[OpTypeBool].push_back(c);
module.mapInstruction(c);
return c->getResultId();
}
Id Builder::makeIntConstant(Id typeId, unsigned value)
{
Id existing = findScalarConstant(OpTypeInt, typeId, value);
if (existing)
return existing;
Instruction* c = new Instruction(getUniqueId(), typeId, OpConstant);
c->addImmediateOperand(value);
constantsTypesGlobals.push_back(c);
groupedConstants[OpTypeInt].push_back(c);
module.mapInstruction(c);
return c->getResultId();
}
Id Builder::makeFloatConstant(float f)
{
Id typeId = makeFloatType(32);
unsigned value = *(unsigned int*)&f;
Id existing = findScalarConstant(OpTypeFloat, typeId, value);
if (existing)
return existing;
Instruction* c = new Instruction(getUniqueId(), typeId, OpConstant);
c->addImmediateOperand(value);
constantsTypesGlobals.push_back(c);
groupedConstants[OpTypeFloat].push_back(c);
module.mapInstruction(c);
return c->getResultId();
}
Id Builder::makeDoubleConstant(double d)
{
// TODO
MissingFunctionality("double constant");
return NoResult;
}
Id Builder::findCompositeConstant(OpCode typeClass, std::vector<Id>& comps) const
{
Instruction* constant;
bool found = false;
for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
constant = groupedConstants[typeClass][i];
// same shape?
if (constant->getNumOperands() != comps.size())
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;
}
// Comments in header
Id Builder::makeCompositeConstant(Id typeId, std::vector<Id>& members)
{
assert(typeId);
OpCode typeClass = getTypeClass(typeId);
switch (typeClass) {
case OpTypeVector:
case OpTypeArray:
case OpTypeStruct:
case OpTypeMatrix:
break;
default:
MissingFunctionality("Constant composite type in Builder");
return makeFloatConstant(0.0);
}
Id existing = findCompositeConstant(typeClass, members);
if (existing)
return existing;
Instruction* c = new Instruction(getUniqueId(), typeId, OpConstantComposite);
for (int op = 0; op < (int)members.size(); ++op)
c->addIdOperand(members[op]);
constantsTypesGlobals.push_back(c);
groupedConstants[typeClass].push_back(c);
module.mapInstruction(c);
return c->getResultId();
}
void Builder::addEntryPoint(ExecutionModel model, Function* function)
{
Instruction* entryPoint = new Instruction(OpEntryPoint);
entryPoint->addImmediateOperand(model);
entryPoint->addIdOperand(function->getId());
entryPoints.push_back(entryPoint);
}
void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value)
{
// TODO: handle multiple optional arguments
Instruction* instr = new Instruction(OpExecutionMode);
instr->addIdOperand(entryPoint->getId());
instr->addImmediateOperand(mode);
if (value >= 0)
instr->addImmediateOperand(value);
executionModes.push_back(instr);
}
void Builder::addName(Id id, const char* string)
{
Instruction* name = new Instruction(OpName);
name->addIdOperand(id);
name->addStringOperand(string);
names.push_back(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(name);
}
void Builder::addLine(Id target, Id fileName, int lineNum, int column)
{
Instruction* line = new Instruction(OpLine);
line->addIdOperand(target);
line->addIdOperand(fileName);
line->addImmediateOperand(lineNum);
line->addImmediateOperand(column);
lines.push_back(line);
}
void Builder::addDecoration(Id id, Decoration decoration, int num)
{
Instruction* dec = new Instruction(OpDecorate);
dec->addIdOperand(id);
dec->addImmediateOperand(decoration);
if (num >= 0)
dec->addImmediateOperand(num);
decorations.push_back(dec);
}
void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num)
{
Instruction* dec = new Instruction(OpMemberDecorate);
dec->addIdOperand(id);
dec->addImmediateOperand(member);
dec->addImmediateOperand(decoration);
if (num >= 0)
dec->addImmediateOperand(num);
decorations.push_back(dec);
}
// Comments in header
Function* Builder::makeMain()
{
assert(! mainFunction);
Block* entry;
std::vector<Id> params;
mainFunction = makeFunctionEntry(makeVoidType(), "main", params, &entry);
stageExit = new Block(getUniqueId(), *mainFunction);
return mainFunction;
}
// Comments in header
void Builder::closeMain()
{
setBuildPoint(stageExit);
stageExit->addInstruction(new Instruction(NoResult, NoType, OpReturn));
mainFunction->addBlock(stageExit);
}
// Comments in header
Function* Builder::makeFunctionEntry(Id returnType, const char* name, std::vector<Id>& paramTypes, Block **entry)
{
Id typeId = makeFunctionType(returnType, paramTypes);
Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds(paramTypes.size());
Function* function = new Function(getUniqueId(), returnType, typeId, firstParamId, module);
if (entry) {
*entry = new Block(getUniqueId(), *function);
function->addBlock(*entry);
setBuildPoint(*entry);
}
if (name)
addName(function->getId(), name);
return function;
}
// Comments in header
void Builder::makeReturn(bool implicit, Id retVal, bool isMain)
{
if (isMain && retVal)
MissingFunctionality("return value from main()");
if (isMain)
createBranch(stageExit);
else if (retVal) {
Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue);
inst->addIdOperand(retVal);
buildPoint->addInstruction(inst);
} else
buildPoint->addInstruction(new Instruction(NoResult, NoType, OpReturn));
if (! implicit)
createAndSetNoPredecessorBlock("post-return");
}
// Comments in header
void Builder::leaveFunction(bool main)
{
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()) {
// Whether we're in an unreachable (non-entry) block.
bool unreachable = function.getEntryBlock() != block && block->getNumPredecessors() == 0;
if (unreachable) {
// Given that this block is at the end of a function, it must be right after an
// explicit return, just remove it.
function.popBlock(block);
} else if (main)
makeMainReturn(true);
else {
// We're get a return instruction at the end of the current block,
// which for a non-void function is really error recovery (?), as the source
// being translated should have had an explicit return, which would have been
// followed by an unreachable block, which was handled above.
if (function.getReturnType() == makeVoidType())
makeReturn(true);
else {
Id retStorage = createVariable(StorageFunction, function.getReturnType(), "dummyReturn");
Id retValue = createLoad(retStorage);
makeReturn(true, retValue);
}
}
}
if (main)
closeMain();
}
// Comments in header
void Builder::makeDiscard()
{
buildPoint->addInstruction(new Instruction(OpKill));
createAndSetNoPredecessorBlock("post-discard");
}
// Comments in header
Id Builder::createVariable(StorageClass storageClass, Id type, const char* name)
{
Id pointerType = makePointer(storageClass, type);
Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable);
inst->addImmediateOperand(storageClass);
switch (storageClass) {
case StorageConstantUniform:
case StorageUniform:
case StorageInput:
case StorageOutput:
case StorageWorkgroupLocal:
case StoragePrivateGlobal:
case StorageWorkgroupGlobal:
constantsTypesGlobals.push_back(inst);
module.mapInstruction(inst);
break;
case StorageFunction:
// Validation rules require the declaration in the entry block
buildPoint->getParent().addLocalVariable(inst);
break;
default:
MissingFunctionality("storage class in createVariable");
break;
}
if (name)
addName(inst->getResultId(), name);
return inst->getResultId();
}
// Comments in header
void Builder::createStore(Id rValue, Id lValue)
{
Instruction* store = new Instruction(OpStore);
store->addIdOperand(lValue);
store->addIdOperand(rValue);
buildPoint->addInstruction(store);
}
// Comments in header
Id Builder::createLoad(Id lValue)
{
Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad);
load->addIdOperand(lValue);
buildPoint->addInstruction(load);
return load->getResultId();
}
// Comments in header
Id Builder::createAccessChain(StorageClass storageClass, Id base, std::vector<Id>& offsets)
{
// Figure out the final resulting type.
spv::Id typeId = getTypeId(base);
assert(isPointerType(typeId) && offsets.size() > 0);
typeId = getContainedTypeId(typeId);
for (int i = 0; i < (int)offsets.size(); ++i) {
if (isStructType(typeId)) {
assert(isConstantScalar(offsets[i]));
typeId = getContainedTypeId(typeId, getConstantScalar(offsets[i]));
} else
typeId = getContainedTypeId(typeId, offsets[i]);
}
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(chain);
return chain->getResultId();
}
Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index)
{
Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
extract->addIdOperand(composite);
extract->addImmediateOperand(index);
buildPoint->addInstruction(extract);
return extract->getResultId();
}
Id Builder::createCompositeExtract(Id composite, Id typeId, std::vector<unsigned>& 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(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(insert);
return insert->getResultId();
}
Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, 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(insert);
return insert->getResultId();
}
Id Builder::createEmptyOp(OpCode opCode)
{
Instruction* op = new Instruction(opCode);
buildPoint->addInstruction(op);
return op->getResultId();
}
void Builder::createControlBarrier(unsigned executionScope)
{
Instruction* op = new Instruction(OpControlBarrier);
op->addImmediateOperand(executionScope);
buildPoint->addInstruction(op);
}
void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics)
{
Instruction* op = new Instruction(OpMemoryBarrier);
op->addImmediateOperand(executionScope);
op->addImmediateOperand(memorySemantics);
buildPoint->addInstruction(op);
}
Id Builder::createUnaryOp(OpCode opCode, Id typeId, Id operand)
{
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
op->addIdOperand(operand);
buildPoint->addInstruction(op);
return op->getResultId();
}
Id Builder::createBinOp(OpCode opCode, Id typeId, Id left, Id right)
{
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
op->addIdOperand(left);
op->addIdOperand(right);
buildPoint->addInstruction(op);
return op->getResultId();
}
Id Builder::createTriOp(OpCode opCode, Id typeId, Id op1, Id op2, Id op3)
{
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
op->addIdOperand(op1);
op->addIdOperand(op2);
op->addIdOperand(op3);
buildPoint->addInstruction(op);
return op->getResultId();
}
Id Builder::createTernaryOp(OpCode opCode, Id typeId, Id op1, Id op2, Id op3)
{
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
op->addIdOperand(op1);
op->addIdOperand(op2);
op->addIdOperand(op3);
buildPoint->addInstruction(op);
return op->getResultId();
}
Id Builder::createFunctionCall(spv::Function* function, 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(op);
return op->getResultId();
}
// Comments in header
Id Builder::createRvalueSwizzle(Id typeId, Id source, std::vector<unsigned>& channels)
{
if (channels.size() == 1)
return createCompositeExtract(source, typeId, channels.front());
Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
swizzle->addIdOperand(source);
swizzle->addIdOperand(source);
for (int i = 0; i < (int)channels.size(); ++i)
swizzle->addImmediateOperand(channels[i]);
buildPoint->addInstruction(swizzle);
return swizzle->getResultId();
}
// Comments in header
Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, std::vector<unsigned>& channels)
{
assert(getNumComponents(source) == channels.size());
if (channels.size() == 1 && getNumComponents(source) == 1)
return createCompositeInsert(source, target, typeId, channels.front());
Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
swizzle->addIdOperand(target);
swizzle->addIdOperand(source);
// Set up an identity shuffle from the base value to the result value
unsigned int components[4];
int numTargetComponents = getNumComponents(target);
for (int i = 0; i < numTargetComponents; ++i)
components[i] = i;
// Punch in the l-value swizzle
for (int i = 0; i < (int)channels.size(); ++i)
components[channels[i]] = numTargetComponents + i;
// finish the instruction with these components selectors
for (int i = 0; i < numTargetComponents; ++i)
swizzle->addImmediateOperand(components[i]);
buildPoint->addInstruction(swizzle);
return swizzle->getResultId();
}
// Comments in header
void Builder::promoteScalar(Decoration precision, Id& left, Id& right)
{
int direction = getNumComponents(right) - getNumComponents(left);
if (direction > 0)
left = smearScalar(precision, left, getTypeId(right));
else if (direction < 0)
right = smearScalar(precision, right, getTypeId(left));
return;
}
// Comments in header
Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType)
{
assert(getNumComponents(scalar) == 1);
int numComponents = getNumTypeComponents(vectorType);
if (numComponents == 1)
return scalar;
Instruction* smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct);
for (int c = 0; c < numComponents; ++c)
smear->addIdOperand(scalar);
buildPoint->addInstruction(smear);
return smear->getResultId();
}
// Comments in header
Id Builder::createBuiltinCall(Decoration precision, Id resultType, Id builtins, int entryPoint, std::vector<Id>& args)
{
Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst);
inst->addIdOperand(builtins);
inst->addImmediateOperand(entryPoint);
for (int arg = 0; arg < (int)args.size(); ++arg)
inst->addIdOperand(args[arg]);
buildPoint->addInstruction(inst);
return inst->getResultId();
}
// Accept all parameters needed to create a texture instruction.
// Create the correct instruction based on the inputs, and make the call.
Id Builder::createTextureCall(Decoration precision, Id resultType, bool proj, const TextureParameters& parameters)
{
static const int maxTextureArgs = 5;
Id texArgs[maxTextureArgs] = {};
//
// Set up the arguments
//
int numArgs = 0;
texArgs[numArgs++] = parameters.sampler;
texArgs[numArgs++] = parameters.coords;
if (parameters.gradX) {
texArgs[numArgs++] = parameters.gradX;
texArgs[numArgs++] = parameters.gradY;
}
if (parameters.lod)
texArgs[numArgs++] = parameters.lod;
if (parameters.offset)
texArgs[numArgs++] = parameters.offset;
if (parameters.bias)
texArgs[numArgs++] = parameters.bias;
if (parameters.Dref)
texArgs[numArgs++] = parameters.Dref;
//
// Set up the instruction
//
OpCode opCode;
if (proj && parameters.gradX && parameters.offset)
opCode = OpTextureSampleProjGradOffset;
else if (proj && parameters.lod && parameters.offset)
opCode = OpTextureSampleProjLodOffset;
else if (parameters.gradX && parameters.offset)
opCode = OpTextureSampleGradOffset;
else if (proj && parameters.offset)
opCode = OpTextureSampleProjOffset;
else if (parameters.lod && parameters.offset)
opCode = OpTextureSampleLodOffset;
else if (proj && parameters.gradX)
opCode = OpTextureSampleProjGrad;
else if (proj && parameters.lod)
opCode = OpTextureSampleProjLod;
else if (parameters.offset)
opCode = OpTextureSampleOffset;
else if (parameters.gradX)
opCode = OpTextureSampleGrad;
else if (proj)
opCode = OpTextureSampleProj;
else if (parameters.lod)
opCode = OpTextureSampleLod;
else if (parameters.Dref)
opCode = OpTextureSampleDref;
else
opCode = OpTextureSample;
Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode);
for (int op = 0; op < numArgs; ++op)
textureInst->addIdOperand(texArgs[op]);
setPrecision(textureInst->getResultId(), precision);
buildPoint->addInstruction(textureInst);
return textureInst->getResultId();
}
// Comments in header
Id Builder::createTextureQueryCall(OpCode opCode, const TextureParameters& parameters)
{
// Figure out the result type
Id resultType;
switch (opCode) {
case OpTextureQuerySize:
case OpTextureQuerySizeLod:
{
int numComponents;
switch (getDimensionality(parameters.sampler)) {
case Dim1D:
case DimBuffer:
numComponents = 1;
break;
case Dim2D:
case DimCube:
case DimRect:
numComponents = 2;
break;
case Dim3D:
numComponents = 3;
break;
default:
MissingFunctionality("texture query dimensionality");
break;
}
if (isArrayedSampler(parameters.sampler))
++numComponents;
if (numComponents == 1)
resultType = makeIntType(32);
else
resultType = makeVectorType(makeIntType(32), numComponents);
break;
}
case OpTextureQueryLod:
resultType = makeVectorType(makeFloatType(32), 2);
break;
case OpTextureQueryLevels:
case OpTextureQuerySamples:
resultType = makeIntType(32);
break;
default:
MissingFunctionality("Texture query op code");
}
Instruction* query = new Instruction(getUniqueId(), resultType, opCode);
query->addIdOperand(parameters.sampler);
if (parameters.coords)
query->addIdOperand(parameters.coords);
if (parameters.lod)
query->addIdOperand(parameters.lod);
buildPoint->addInstruction(query);
return query->getResultId();
}
// Comments in header
//Id Builder::createSamplePositionCall(Decoration precision, Id returnType, Id sampleIdx)
//{
// // Return type is only flexible type
// Function* opCode = (fSamplePosition, returnType);
//
// Instruction* instr = (opCode, sampleIdx);
// setPrecision(instr, precision);
//
// return instr;
//}
// Comments in header
//Id Builder::createBitFieldExtractCall(Decoration precision, Id id, Id offset, Id bits, bool isSigned)
//{
// OpCode opCode = isSigned ? sBitFieldExtract
// : uBitFieldExtract;
//
// if (isScalar(offset) == false || isScalar(bits) == false)
// MissingFunctionality("bitFieldExtract operand types");
//
// // Dest and value are matching flexible types
// Function* opCode = (opCode, id->getType(), id->getType());
//
// assert(opCode);
//
// Instruction* instr = (opCode, id, offset, bits);
// setPrecision(instr, precision);
//
// return instr;
//}
// Comments in header
//Id Builder::createBitFieldInsertCall(Decoration precision, Id base, Id insert, Id offset, Id bits)
//{
// OpCode opCode = bitFieldInsert;
//
// if (isScalar(offset) == false || isScalar(bits) == false)
// MissingFunctionality("bitFieldInsert operand types");
//
// // Dest, base, and insert are matching flexible types
// Function* opCode = (opCode, base->getType(), base->getType(), base->getType());
//
// assert(opCode);
//
// Instruction* instr = (opCode, base, insert, offset, bits);
// setPrecision(instr, precision);
//
// return instr;
//}
// Comments in header
Id Builder::createCompare(Decoration precision, Id value1, Id value2, bool equal)
{
Instruction* compare = 0;
spv::OpCode binOp = spv::OpNop;
Id boolType = makeBoolType();
Id valueType = getTypeId(value1);
assert(valueType == getTypeId(value2));
assert(! isScalar(value1));
// Vectors
if (isVectorType(valueType)) {
Id boolVectorType = makeVectorType(boolType, getNumTypeComponents(valueType));
Id boolVector;
OpCode op;
if (getMostBasicTypeClass(valueType) == OpTypeFloat)
op = equal ? OpFOrdEqual : OpFOrdNotEqual;
else
op = equal ? OpIEqual : OpINotEqual;
boolVector = createBinOp(op, boolVectorType, value1, value2);
setPrecision(boolVector, precision);
// Reduce vector compares with any() and all().
op = equal ? OpAll : OpAny;
return createUnaryOp(op, boolType, boolVector);
}
spv::MissingFunctionality("Composite comparison of non-vectors");
return NoResult;
// Recursively handle aggregates, which include matrices, arrays, and structures
// and accumulate the results.
// Matrices
// Arrays
//int numElements;
//const llvm::ArrayType* arrayType = llvm::dyn_cast<llvm::ArrayType>(value1->getType());
//if (arrayType)
// numElements = (int)arrayType->getNumElements();
//else {
// // better be structure
// const llvm::StructType* structType = llvm::dyn_cast<llvm::StructType>(value1->getType());
// assert(structType);
// numElements = structType->getNumElements();
//}
//assert(numElements > 0);
//for (int element = 0; element < numElements; ++element) {
// // Get intermediate comparison values
// llvm::Value* element1 = builder.CreateExtractValue(value1, element, "element1");
// setInstructionPrecision(element1, precision);
// llvm::Value* element2 = builder.CreateExtractValue(value2, element, "element2");
// setInstructionPrecision(element2, precision);
// llvm::Value* subResult = createCompare(precision, element1, element2, equal, "comp");
// // Accumulate intermediate comparison
// if (element == 0)
// result = subResult;
// else {
// if (equal)
// result = builder.CreateAnd(result, subResult);
// else
// result = builder.CreateOr(result, subResult);
// setInstructionPrecision(result, precision);
// }
//}
//return result;
}
// Comments in header
//Id Builder::createOperation(Decoration precision, OpCode opCode, Id operand)
//{
// OpCode* opCode = 0;
//
// // Handle special return types here. Things that don't have same result type as parameter
// switch (opCode) {
// case fIsNan:
// case fIsInf:
// break;
// case fFloatBitsToInt:
// break;
// case fIntBitsTofloat:
// break;
// case fPackSnorm2x16:
// case fPackUnorm2x16:
// case fPackHalf2x16:
// break;
// case fUnpackUnorm2x16:
// case fUnpackSnorm2x16:
// case fUnpackHalf2x16:
// break;
//
// case fFrexp:
// case fLdexp:
// case fPackUnorm4x8:
// case fPackSnorm4x8:
// case fUnpackUnorm4x8:
// case fUnpackSnorm4x8:
// case fPackDouble2x32:
// case fUnpackDouble2x32:
// break;
// case fLength:
// // scalar result type
// break;
// case any:
// case all:
// // fixed result type
// break;
// case fModF:
// // modf() will return a struct that the caller must decode
// break;
// default:
// // Unary operations that have operand and dest with same flexible type
// break;
// }
//
// assert(opCode);
//
// Instruction* instr = (opCode, operand);
// setPrecision(instr, precision);
//
// return instr;
//}
//
//// Comments in header
//Id Builder::createOperation(Decoration precision, OpCode opCode, Id operand0, Id operand1)
//{
// Function* opCode = 0;
//
// // Handle special return types here. Things that don't have same result type as parameter
// switch (opCode) {
// case fDistance:
// case fDot2:
// case fDot3:
// case fDot4:
// // scalar result type
// break;
// case fStep:
// // first argument can be scalar, return and second argument match
// break;
// case fSmoothStep:
// // first argument can be scalar, return and second argument match
// break;
// default:
// // Binary operations that have operand and dest with same flexible type
// break;
// }
//
// assert(opCode);
//
// Instruction* instr = (opCode, operand0, operand1);
// setPrecision(instr, precision);
//
// return instr;
//}
//
//Id Builder::createOperation(Decoration precision, OpCode opCode, Id operand0, Id operand1, Id operand2)
//{
// Function* opCode;
//
// // Handle special return types here. Things that don't have same result type as parameter
// switch (opCode) {
// case fSmoothStep:
// // first argument can be scalar, return and second argument match
// break;
// default:
// // Use operand0 type as result type
// break;
// }
//
// assert(opCode);
//
// Instruction* instr = (opCode, operand0, operand1, operand2);
// setPrecision(instr, precision);
//
// return instr;
//}
// OpCompositeConstruct
Id Builder::createCompositeConstruct(Id typeId, std::vector<Id>& constituents)
{
assert(isAggregateType(typeId) || getNumTypeComponents(typeId) > 1 && getNumTypeComponents(typeId) == constituents.size());
Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct);
for (int c = 0; c < (int)constituents.size(); ++c)
op->addIdOperand(constituents[c]);
buildPoint->addInstruction(op);
return op->getResultId();
}
// Vector or scalar constructor
Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
{
Id result;
unsigned int numTargetComponents = getNumTypeComponents(resultTypeId);
unsigned int targetComponent = 0;
// Special case: when calling a vector constructor with a single scalar
// argument, smear the scalar
if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1)
return smearScalar(precision, sources[0], resultTypeId);
Id scalarTypeId = getScalarTypeId(resultTypeId);
std::vector<Id> constituents; // accumulate the arguments for OpCompositeConstruct
for (unsigned int i = 0; i < sources.size(); ++i) {
if (isAggregate(sources[i]))
MissingFunctionality("aggregate in vector constructor");
unsigned int sourceSize = getNumComponents(sources[i]);
unsigned int sourcesToUse = sourceSize;
if (sourcesToUse + targetComponent > numTargetComponents)
sourcesToUse = numTargetComponents - targetComponent;
for (unsigned int s = 0; s < sourcesToUse; ++s) {
Id arg = sources[i];
if (sourceSize > 1) {
std::vector<unsigned> swiz;
swiz.push_back(s);
arg = createRvalueSwizzle(scalarTypeId, arg, swiz);
}
if (numTargetComponents > 1)
constituents.push_back(arg);
else
result = arg;
++targetComponent;
}
if (targetComponent >= numTargetComponents)
break;
}
if (constituents.size() > 0)
result = createCompositeConstruct(resultTypeId, constituents);
setPrecision(result, precision);
return result;
}
// Comments in header
Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
{
Id componentTypeId = getScalarTypeId(resultTypeId);
int numCols = getTypeNumColumns(resultTypeId);
int numRows = getTypeNumRows(resultTypeId);
// Will use a two step process
// 1. make a compile-time 2D array of values
// 2. construct a matrix from that array
// Step 1.
// initialize the array to the identity matrix
Id ids[maxMatrixSize][maxMatrixSize];
Id one = makeFloatConstant(1.0);
Id zero = makeFloatConstant(0.0);
for (int col = 0; col < 4; ++col) {
for (int row = 0; row < 4; ++row) {
if (col == row)
ids[col][row] = one;
else
ids[col][row] = zero;
}
}
// modify components as dictated by the arguments
if (sources.size() == 1 && isScalar(sources[0])) {
// a single scalar; resets the diagonals
for (int col = 0; col < 4; ++col)
ids[col][col] = sources[0];
} else if (isMatrix(sources[0])) {
// constructing from another matrix; copy over the parts that exist in both the argument and constructee
Id matrix = sources[0];
int minCols = std::min(numCols, getNumColumns(matrix));
int minRows = std::min(numRows, getNumRows(matrix));
for (int col = 0; col < minCols; ++col) {
std::vector<unsigned> indexes;
indexes.push_back(col);
for (int row = 0; row < minRows; ++row) {
indexes.push_back(row);
ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes);
indexes.pop_back();
setPrecision(ids[col][row], precision);
}
}
} else {
// fill in the matrix in column-major order with whatever argument components are available
int row = 0;
int col = 0;
for (int arg = 0; arg < (int)sources.size(); ++arg) {
Id argComp = sources[arg];
for (int comp = 0; comp < getNumComponents(sources[arg]); ++comp) {
if (getNumComponents(sources[arg]) > 1) {
argComp = createCompositeExtract(sources[arg], componentTypeId, comp);
setPrecision(argComp, precision);
}
ids[col][row++] = argComp;
if (row == numRows) {
row = 0;
col++;
}
}
}
}
// Step 2: Construct a matrix from that array.
// First make the column vectors, then make the matrix.
// make the column vectors
Id columnTypeId = getContainedTypeId(resultTypeId);
std::vector<Id> matrixColumns;
for (int col = 0; col < numCols; ++col) {
std::vector<Id> vectorComponents;
for (int row = 0; row < numRows; ++row)
vectorComponents.push_back(ids[col][row]);
matrixColumns.push_back(createCompositeConstruct(columnTypeId, vectorComponents));
}
// make the matrix
return createCompositeConstruct(resultTypeId, matrixColumns);
}
// Comments in header
Builder::If::If(Id cond, Builder& gb) :
builder(gb),
condition(cond),
elseBlock(0)
{
function = &builder.getBuildPoint()->getParent();
// make the blocks, but only put the then-block into the function,
// the else-block and merge-block will be added later, in order, after
// earlier code is emitted
thenBlock = new Block(builder.getUniqueId(), *function);
mergeBlock = new Block(builder.getUniqueId(), *function);
// Save the current block, so that we can add in the flow control split when
// makeEndIf is called.
headerBlock = builder.getBuildPoint();
function->addBlock(thenBlock);
builder.setBuildPoint(thenBlock);
}
// Comments in header
void Builder::If::makeBeginElse()
{
// Close out the "then" by having it jump to the mergeBlock
builder.createBranch(mergeBlock);
// Make the first else block and add it to the function
elseBlock = new Block(builder.getUniqueId(), *function);
function->addBlock(elseBlock);
// Start building the else block
builder.setBuildPoint(elseBlock);
}
// Comments in header
void Builder::If::makeEndIf()
{
// jump to the merge block
builder.createBranch(mergeBlock);
// Go back to the headerBlock and make the flow control split
builder.setBuildPoint(headerBlock);
builder.createMerge(OpSelectionMerge, mergeBlock, SelectControlNone);
if (elseBlock)
builder.createConditionalBranch(condition, thenBlock, elseBlock);
else
builder.createConditionalBranch(condition, thenBlock, mergeBlock);
// add the merge block to the function
function->addBlock(mergeBlock);
builder.setBuildPoint(mergeBlock);
}
// Comments in header
void Builder::makeSwitch(Id selector, int numSegments, std::vector<int>& caseValues, std::vector<int>& valueIndexToSegment, int defaultSegment,
std::vector<Block*>& segmentBlocks)
{
Function& function = buildPoint->getParent();
// make all the blocks
for (int s = 0; s < numSegments; ++s)
segmentBlocks.push_back(new Block(getUniqueId(), function));
Block* mergeBlock = new Block(getUniqueId(), function);
// make and insert the switch's selection-merge instruction
createMerge(OpSelectionMerge, mergeBlock, SelectControlNone);
// make the switch instruction
Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch);
switchInst->addIdOperand(selector);
switchInst->addIdOperand(defaultSegment >= 0 ? segmentBlocks[defaultSegment]->getId() : mergeBlock->getId());
for (int i = 0; i < (int)caseValues.size(); ++i) {
switchInst->addImmediateOperand(caseValues[i]);
switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId());
}
buildPoint->addInstruction(switchInst);
// push the merge block
switchMerges.push(mergeBlock);
}
// Comments in header
void Builder::addSwitchBreak()
{
// branch to the top of the merge block stack
createBranch(switchMerges.top());
}
// Comments in header
void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment)
{
int lastSegment = nextSegment - 1;
if (lastSegment >= 0) {
// Close out previous segment by jumping, if necessary, to next segment
if (! buildPoint->isTerminated())
createBranch(segmentBlock[nextSegment]);
}
Block* block = segmentBlock[nextSegment];
block->getParent().addBlock(block);
setBuildPoint(block);
}
// Comments in header
void Builder::endSwitch(std::vector<Block*>& segmentBlock)
{
// Close out previous segment by jumping, if necessary, to next segment
if (! buildPoint->isTerminated())
addSwitchBreak();
switchMerges.top()->getParent().addBlock(switchMerges.top());
setBuildPoint(switchMerges.top());
switchMerges.pop();
}
// Comments in header
void Builder::makeNewLoop()
{
Loop loop = { };
loop.function = &getBuildPoint()->getParent();
loop.header = new Block(getUniqueId(), *loop.function);
loop.merge = new Block(getUniqueId(), *loop.function);
loops.push(loop);
// Branch into the loop
createBranch(loop.header);
// Set ourselves inside the loop
loop.function->addBlock(loop.header);
setBuildPoint(loop.header);
}
void Builder::createLoopHeaderBranch(Id condition)
{
Loop loop = loops.top();
Block* body = new Block(getUniqueId(), *loop.function);
createMerge(OpLoopMerge, loop.merge, LoopControlNone);
createConditionalBranch(condition, body, loop.merge);
loop.function->addBlock(body);
setBuildPoint(body);
}
// Add a back-edge (e.g "continue") for the innermost loop that you're in
void Builder::createLoopBackEdge(bool implicit)
{
Loop loop = loops.top();
// Just branch back, and set up a block for dead code if it's a user continue
createBranch(loop.header);
if (! implicit)
createAndSetNoPredecessorBlock("post-loop-continue");
}
// Add an exit (e.g. "break") for the innermost loop that you're in
void Builder::createLoopExit()
{
createBranch(loops.top().merge);
createAndSetNoPredecessorBlock("post-loop-break");
}
// Close the innermost loop
void Builder::closeLoop()
{
// Branch back to the top
createLoopBackEdge(true);
// Add the merge block and set the build point to it
Loop loop = loops.top();
loop.function->addBlock(loop.merge);
setBuildPoint(loop.merge);
loops.pop();
}
void Builder::clearAccessChain()
{
accessChain.base = 0;
accessChain.indexChain.clear();
accessChain.instr = 0;
accessChain.swizzle.clear();
accessChain.component = 0;
accessChain.swizzleTargetWidth = 0;
accessChain.resultType = NoType;
accessChain.isRValue = false;
}
// Comments in header
void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, int width, Id type)
{
// if needed, propagate the swizzle for the current access chain
if (accessChain.swizzle.size()) {
std::vector<unsigned> oldSwizzle = accessChain.swizzle;
accessChain.swizzle.resize(0);
for (unsigned int i = 0; i < swizzle.size(); ++i) {
accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]);
}
} else {
accessChain.swizzle = swizzle;
}
// track width the swizzle operates on; once known, it does not change
if (accessChain.swizzleTargetWidth == 0)
accessChain.swizzleTargetWidth = width;
accessChain.resultType = type;
// determine if we need to track this swizzle anymore
simplifyAccessChainSwizzle();
}
// Comments in header
void Builder::accessChainStore(Id rvalue)
{
assert(accessChain.isRValue == false);
Id base = collapseAccessChain();
// If swizzle exists, it is out-of-order or not full, we must load the target vector,
// extract and insert elements to perform writeMask and/or swizzle.
Id source;
if (accessChain.swizzle.size()) {
Id tempBaseId = createLoad(base);
source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, rvalue, accessChain.swizzle);
} else if (accessChain.component) {
Instruction* vectorInsert = new Instruction(getUniqueId(), getTypeId(rvalue), OpVectorInsertDynamic);
vectorInsert->addIdOperand(createLoad(base));
vectorInsert->addIdOperand(rvalue);
vectorInsert->addIdOperand(accessChain.component);
buildPoint->addInstruction(vectorInsert);
source = vectorInsert->getResultId();
} else
source = rvalue;
createStore(source, base);
}
// Comments in header
Id Builder::accessChainLoad(Decoration precision)
{
Id id;
if (accessChain.isRValue) {
if (accessChain.indexChain.size() > 0) {
// if all the accesses are constants, we can use OpCompositeExtract
std::vector<unsigned> indexes;
bool constant = true;
for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
if (isConstantScalar(accessChain.indexChain[i]))
indexes.push_back(getConstantScalar(accessChain.indexChain[i]));
else {
constant = false;
break;
}
}
if (constant)
id = createCompositeExtract(accessChain.base, accessChain.resultType, indexes);
else {
// make a new function variable for this r-value
Id lValue = createVariable(StorageFunction, getTypeId(accessChain.base), "indexable");
// store into it
createStore(accessChain.base, lValue);
// move base to the new variable
accessChain.base = lValue;
accessChain.isRValue = false;
// load through the access chain
id = createLoad(collapseAccessChain());
}
} else
id = accessChain.base;
} else {
// load through the access chain
id = createLoad(collapseAccessChain());
}
if (accessChain.component) {
Instruction* vectorExtract = new Instruction(getUniqueId(), getScalarTypeId(getTypeId(id)), OpVectorExtractDynamic);
vectorExtract->addIdOperand(id);
vectorExtract->addIdOperand(accessChain.component);
buildPoint->addInstruction(vectorExtract);
id = vectorExtract->getResultId();
} else if (accessChain.swizzle.size())
id = createRvalueSwizzle(accessChain.resultType, id, accessChain.swizzle);
return id;
}
Id Builder::accessChainGetLValue()
{
assert(accessChain.isRValue == false);
Id lvalue = collapseAccessChain();
// If swizzle exists, it is out-of-order or not full, we must load the target vector,
// extract and insert elements to perform writeMask and/or swizzle. This does not
// go with getting a direct l-value pointer.
assert(accessChain.swizzle.size() == 0);
assert(accessChain.component == spv::NoResult);
return lvalue;
}
void Builder::dump(std::vector<unsigned int>& out) const
{
// Header, before first instructions:
out.push_back(MagicNumber);
out.push_back(Version);
out.push_back(builderNumber);
out.push_back(uniqueId + 1);
out.push_back(0);
// First instructions, some created on the spot here:
if (source != LangUnknown) {
Instruction sourceInst(0, 0, OpSource);
sourceInst.addImmediateOperand(source);
sourceInst.addImmediateOperand(sourceVersion);
sourceInst.dump(out);
}
for (int e = 0; e < (int)extensions.size(); ++e) {
Instruction extInst(0, 0, OpSourceExtension);
extInst.addStringOperand(extensions[e]);
extInst.dump(out);
}
// TBD: OpExtension ...
dumpInstructions(out, imports);
Instruction memInst(0, 0, OpMemoryModel);
memInst.addImmediateOperand(addressModel);
memInst.addImmediateOperand(memoryModel);
memInst.dump(out);
// Instructions saved up while building:
dumpInstructions(out, entryPoints);
dumpInstructions(out, executionModes);
dumpInstructions(out, names);
dumpInstructions(out, lines);
dumpInstructions(out, decorations);
dumpInstructions(out, constantsTypesGlobals);
dumpInstructions(out, externals);
// The functions
module.dump(out);
}
//
// Protected methods.
//
Id Builder::collapseAccessChain()
{
// TODO: bring in an individual component swizzle here, so that a pointer
// all the way to the component level can be created.
assert(accessChain.isRValue == false);
if (accessChain.indexChain.size() > 0) {
if (accessChain.instr == 0) {
StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base));
accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain);
}
return accessChain.instr;
} else
return accessChain.base;
}
// clear out swizzle if it is redundant
void Builder::simplifyAccessChainSwizzle()
{
// if swizzle has fewer components than our target, it is a writemask
if (accessChain.swizzleTargetWidth > (int)accessChain.swizzle.size())
return;
// if components are out of order, it is a swizzle
for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {
if (i != accessChain.swizzle[i])
return;
}
// otherwise, there is no need to track this swizzle
accessChain.swizzle.clear();
accessChain.swizzleTargetWidth = 0;
}
// Utility method for creating a new block and setting the insert point to
// be in it. This is useful for flow-control operations that need a "dummy"
// block proceeding them (e.g. instructions after a discard, etc).
void Builder::createAndSetNoPredecessorBlock(const char* name)
{
Block* block = new Block(getUniqueId(), buildPoint->getParent());
buildPoint->getParent().addBlock(block);
setBuildPoint(block);
if (name)
addName(block->getId(), name);
}
// Comments in header
void Builder::createBranch(Block* block)
{
Instruction* branch = new Instruction(OpBranch);
branch->addIdOperand(block->getId());
buildPoint->addInstruction(branch);
block->addPredecessor(buildPoint);
}
void Builder::createMerge(OpCode mergeCode, Block* mergeBlock, unsigned int control)
{
Instruction* merge = new Instruction(mergeCode);
merge->addIdOperand(mergeBlock->getId());
merge->addImmediateOperand(control);
buildPoint->addInstruction(merge);
}
void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock)
{
Instruction* branch = new Instruction(OpBranchConditional);
branch->addIdOperand(condition);
branch->addIdOperand(thenBlock->getId());
branch->addIdOperand(elseBlock->getId());
buildPoint->addInstruction(branch);
thenBlock->addPredecessor(buildPoint);
elseBlock->addPredecessor(buildPoint);
}
void Builder::dumpInstructions(std::vector<unsigned int>& out, const std::vector<Instruction*>& instructions) const
{
for (int i = 0; i < (int)instructions.size(); ++i) {
instructions[i]->dump(out);
}
}
void MissingFunctionality(const char* fun)
{
printf("Missing functionality: %s\n", fun);
exit(1);
}
void ValidationError(const char* error)
{
printf("Validation Error: %s\n", error);
}
}; // end spv namespace
//
//Copyright (C) 2014 LunarG, Inc.
//
//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.
//
// Author: John Kessenich, LunarG
//
//
// "Builder" is an interface to fully build SPIR-V IR. Allocate one of
// these to build (a thread safe) internal SPIR-V representation (IR),
// and then dump it as a binary stream according to the SPIR-V specification.
//
// A Builder has a 1:1 relationship with a SPIR-V module.
//
#pragma once
#ifndef SpvBuilder_H
#define SpvBuilder_H
#include "spirv.h"
#include "spvIR.h"
#include <algorithm>
#include <stack>
#include <map>
namespace spv {
class Builder {
public:
Builder(unsigned int userNumber);
virtual ~Builder();
static const int maxMatrixSize = 4;
void setSource(spv::SourceLanguage lang, int version)
{
source = lang;
sourceVersion = version;
}
void addSourceExtension(const char* ext) { extensions.push_back(ext); }
Id import(const char*);
void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem)
{
addressModel = addr;
memoryModel = mem;
}
// To get a new <id> for anything needing a new one.
Id getUniqueId() { return ++uniqueId; }
// To get a set of new <id>s, e.g., for a set of function parameters
Id getUniqueIds(int numIds)
{
Id id = uniqueId + 1;
uniqueId += numIds;
return id;
}
// For creating new types (will return old type if the requested one was already made).
Id makeVoidType();
Id makeBoolType();
Id makePointer(StorageClass, Id type);
Id makeIntegerType(int width, bool hasSign); // generic
Id makeIntType(int width) { return makeIntegerType(width, true); }
Id makeUintType(int width) { return makeIntegerType(width, false); }
Id makeFloatType(int width);
Id makeStructType(std::vector<Id>& members, const char*);
Id makeVectorType(Id component, int size);
Id makeMatrixType(Id component, int cols, int rows);
Id makeArrayType(Id element, unsigned size);
Id makeFunctionType(Id returnType, std::vector<Id>& paramTypes);
enum samplerContent {
samplerContentTexture,
samplerContentImage,
samplerContentTextureFilter
};
Id makeSampler(Id sampledType, Dimensionality, samplerContent, bool arrayed, bool shadow, bool ms);
// For querying about types.
Id getTypeId(Id resultId) const { return module.getTypeId(resultId); }
Id getDerefTypeId(Id resultId) const;
OpCode getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); }
OpCode getTypeClass(Id typeId) const { return getOpCode(typeId); }
OpCode getMostBasicTypeClass(Id typeId) const;
int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); }
int getNumTypeComponents(Id typeId) const;
Id getScalarTypeId(Id typeId) const;
Id getContainedTypeId(Id typeId) const;
Id getContainedTypeId(Id typeId, int) const;
bool isPointer(Id resultId) const { return isPointerType(getTypeId(resultId)); }
bool isScalar(Id resultId) const { return isScalarType(getTypeId(resultId)); }
bool isVector(Id resultId) const { return isVectorType(getTypeId(resultId)); }
bool isMatrix(Id resultId) const { return isMatrixType(getTypeId(resultId)); }
bool isAggregate(Id resultId) const { return isAggregateType(getTypeId(resultId)); }
bool isPointerType(Id typeId) const { return getTypeClass(typeId) == OpTypePointer; }
bool isScalarType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt || getTypeClass(typeId) == OpTypeBool; }
bool isVectorType(Id typeId) const { return getTypeClass(typeId) == OpTypeVector; }
bool isMatrixType(Id typeId) const { return getTypeClass(typeId) == OpTypeMatrix; }
bool isStructType(Id typeId) const { return getTypeClass(typeId) == OpTypeStruct; }
bool isArrayType(Id typeId) const { return getTypeClass(typeId) == OpTypeArray; }
bool isAggregateType(Id typeId) const { return isArrayType(typeId) || isStructType(typeId); }
bool isSamplerType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampler; }
bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }
unsigned int getConstantScalar(Id resultId) const { return module.getInstruction(resultId)->getImmediateOperand(0); }
int getTypeNumColumns(Id typeId) const
{
assert(isMatrixType(typeId));
return getNumTypeComponents(typeId);
}
int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); }
int getTypeNumRows(Id typeId) const
{
assert(isMatrixType(typeId));
return getNumTypeComponents(getContainedTypeId(typeId));
}
int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); }
Dimensionality getDimensionality(Id resultId) const
{
assert(isSamplerType(getTypeId(resultId)));
return (Dimensionality)module.getInstruction(getTypeId(resultId))->getImmediateOperand(1);
}
bool isArrayedSampler(Id resultId) const
{
assert(isSamplerType(getTypeId(resultId)));
return module.getInstruction(getTypeId(resultId))->getImmediateOperand(3) != 0;
}
// For making new constants (will return old constant if the requested one was already made).
Id makeBoolConstant(bool b);
Id makeIntConstant(Id typeId, unsigned value);
Id makeIntConstant(int i) { return makeIntConstant(makeIntType(32), (unsigned)i); }
Id makeUintConstant(unsigned u) { return makeIntConstant(makeUintType(32), u); }
Id makeFloatConstant(float f);
Id makeDoubleConstant(double d);
// Turn the array of constants into a proper spv constant of the requested type.
Id makeCompositeConstant(Id type, std::vector<Id>& comps);
// Methods for adding information outside the CFG.
void addEntryPoint(ExecutionModel, Function*);
void addExecutionMode(Function*, ExecutionMode mode, int value = -1);
void addName(Id, const char* name);
void addMemberName(Id, int member, const char* name);
void addLine(Id target, Id fileName, int line, int column);
void addDecoration(Id, Decoration, int num = -1);
void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
// At the end of what block do the next create*() instructions go?
void setBuildPoint(Block* bp) { buildPoint = bp; }
Block* getBuildPoint() const { return buildPoint; }
// Make the main function.
Function* makeMain();
// Return from main. Implicit denotes a return at the very end of main.
void makeMainReturn(bool implicit = false) { makeReturn(implicit, 0, true); }
// Close the main function.
void closeMain();
// Make a shader-style function, and create its entry block if entry is non-zero.
// Return the function, pass back the entry.
Function* makeFunctionEntry(Id returnType, const char* name, std::vector<Id>& paramTypes, Block **entry = 0);
// Create a return. Pass whether it is a return form main, and the return
// value (if applicable). In the case of an implicit return, no post-return
// block is inserted.
void makeReturn(bool implicit = false, Id retVal = 0, bool isMain = false);
// Generate all the code needed to finish up a function.
void leaveFunction(bool main);
// Create a discard.
void makeDiscard();
// Create a global or function local or IO variable.
Id createVariable(StorageClass, Id type, const char* name = 0);
// Store into an Id and return the l-value
void createStore(Id rValue, Id lValue);
// Load from an Id and return it
Id createLoad(Id lValue);
// Create an OpAccessChain instruction
Id createAccessChain(StorageClass, Id base, std::vector<Id>& offsets);
// Create an OpCompositeExtract instruction
Id createCompositeExtract(Id composite, Id typeId, unsigned index);
Id createCompositeExtract(Id composite, Id typeId, std::vector<unsigned>& indexes);
Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
Id createCompositeInsert(Id object, Id composite, Id typeId, std::vector<unsigned>& indexes);
Id createEmptyOp(OpCode);
void createControlBarrier(unsigned executionScope);
void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
Id createUnaryOp(OpCode, Id typeId, Id operand);
Id createBinOp(OpCode, Id typeId, Id operand1, Id operand2);
Id createTriOp(OpCode, Id typeId, Id operand1, Id operand2, Id operand3);
Id createTernaryOp(OpCode, Id typeId, Id operand1, Id operand2, Id operand3);
Id createFunctionCall(spv::Function*, std::vector<spv::Id>&);
// Take an rvalue (source) and a set of channels to extract from it to
// make a new rvalue, which is returned.
Id createRvalueSwizzle(Id typeId, Id source, std::vector<unsigned>& channels);
// Take a copy of an lvalue (target) and a source of components, and set the
// source components into the lvalue where the 'channels' say to put them.
// An update version of the target is returned.
// (No true lvalue or stores are used.)
Id createLvalueSwizzle(Id typeId, Id target, Id source, std::vector<unsigned>& channels);
// If the value passed in is an instruction and the precision is not EMpNone,
// it gets tagged with the requested precision.
void setPrecision(Id value, Decoration precision)
{
// TODO
}
// Can smear a scalar to a vector for the following forms:
// - promoteScalar(scalar, vector) // smear scalar to width of vector
// - promoteScalar(vector, scalar) // smear scalar to width of vector
// - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to
// - promoteScalar(scalar, scalar) // do nothing
// Other forms are not allowed.
//
// Note: One of the arguments will change, with the result coming back that way rather than
// through the return value.
void promoteScalar(Decoration precision, Id& left, Id& right);
// make a value by smearing the scalar to fill the type
Id smearScalar(Decoration precision, Id scalarVal, Id);
// Create a call to a built-in function.
Id createBuiltinCall(Decoration precision, Id resultType, Id builtins, int entryPoint, std::vector<Id>& args);
// List of parameters used to create a texture operation
struct TextureParameters {
Id sampler;
Id coords;
Id bias;
Id lod;
Id Dref;
Id offset;
Id gradX;
Id gradY;
};
// Select the correct texture operation based on all inputs, and emit the correct instruction
Id createTextureCall(Decoration precision, Id resultType, bool proj, const TextureParameters&);
// Emit the OpTextureQuery* instruction that was passed in.
// Figure out the right return value and type, and return it.
Id createTextureQueryCall(OpCode, const TextureParameters&);
Id createSamplePositionCall(Decoration precision, Id, Id);
Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned);
Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id);
// Reduction comparision for composites: For equal and not-equal resulting in a scalar.
Id createCompare(Decoration precision, Id, Id, bool /* true if for equal, fales if for not-equal */);
// OpCompositeConstruct
Id createCompositeConstruct(Id typeId, std::vector<Id>& constituents);
// vector or scalar constructor
Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId);
// matrix constructor
Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee);
// Helper to use for building nested control flow with if-then-else.
class If {
public:
If(Id condition, Builder& builder);
~If() {}
void makeBeginElse();
void makeEndIf();
private:
Builder& builder;
Id condition;
Function* function;
Block* headerBlock;
Block* thenBlock;
Block* elseBlock;
Block* mergeBlock;
};
// Make a switch statement. A switch has 'numSegments' of pieces of code, not containing
// any case/default labels, all separated by one or more case/default labels. Each possible
// case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this
// number space. How to compute the value is given by 'condition', as in switch(condition).
//
// The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.
//
// Use a defaultSegment < 0 if there is no default segment (to branch to post switch).
//
// Returns the right set of basic blocks to start each code segment with, so that the caller's
// recursion stack can hold the memory for it.
//
void makeSwitch(Id condition, int numSegments, std::vector<int>& caseValues, std::vector<int>& valueToSegment, int defaultSegment,
std::vector<Block*>& segmentBB); // return argument
// Add a branch to the innermost switch's merge block.
void addSwitchBreak();
// Move to the next code segment, passing in the return argument in makeSwitch()
void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment);
// Finish off the innermost switch.
void endSwitch(std::vector<Block*>& segmentBB);
// Start the beginning of a new loop.
void makeNewLoop();
// Add the branch at the end of the loop header, and leave the build position
// in the first block of the body.
// 'condition' is true if should exit the loop
void createLoopHeaderBranch(Id condition);
// Add a back-edge (e.g "continue") for the innermost loop that you're in
void createLoopBackEdge(bool implicit=false);
// Add an exit (e.g. "break") for the innermost loop that you're in
void createLoopExit();
// Close the innermost loop that you're in
void closeLoop();
//
// Access chain design for an R-Value vs. L-Value:
//
// There is a single access chain the builder is building at
// any particular time. Such a chain can be used to either to a load or
// a store, when desired.
//
// Expressions can be r-values, l-values, or both, or only r-values:
// a[b.c].d = .... // l-value
// ... = a[b.c].d; // r-value, that also looks like an l-value
// ++a[b.c].d; // r-value and l-value
// (x + y)[2]; // r-value only, can't possibly be l-value
//
// Computing an r-value means generating code. Hence,
// r-values should only be computed when they are needed, not speculatively.
//
// Computing an l-value means saving away information for later use in the compiler,
// no code is generated until the l-value is later dereferenced. It is okay
// to speculatively generate an l-value, just not okay to speculatively dereference it.
//
// The base of the access chain (the left-most variable or expression
// from which everything is based) can be set either as an l-value
// or as an r-value. Most efficient would be to set an l-value if one
// is available. If an expression was evaluated, the resulting r-value
// can be set as the chain base.
//
// The users of this single access chain can save and restore if they
// want to nest or manage multiple chains.
//
struct AccessChain {
Id base; // for l-values, pointer to the base object, for r-values, the base object
std::vector<Id> indexChain;
Id instr; // the instruction that generates this access chain
std::vector<unsigned> swizzle;
Id component; // a dynamic component index
int swizzleTargetWidth;
Id resultType; // dereferenced type, to be inclusive of swizzles, which can't have a pointer
bool isRValue;
};
//
// the SPIR-V builder maintains a single active chain that
// the following methods operated on
//
// for external save and restore
AccessChain getAccessChain() { return accessChain; }
void setAccessChain(AccessChain newChain) { accessChain = newChain; }
// clear accessChain
void clearAccessChain();
// set new base as an l-value base
void setAccessChainLValue(Id lValue)
{
assert(isPointer(lValue));
accessChain.base = lValue;
}
// set new base value as an r-value
void setAccessChainRValue(Id rValue)
{
accessChain.isRValue = true;
accessChain.base = rValue;
accessChain.resultType = getTypeId(rValue);
}
// push offset onto the end of the chain
void accessChainPush(Id offset, Id newType)
{
accessChain.indexChain.push_back(offset);
accessChain.resultType = newType;
}
// push new swizzle onto the end of any existing swizzle, merging into a single swizzle
void accessChainPushSwizzle(std::vector<unsigned>& swizzle, int width, Id type);
// push a variable component selection onto the access chain; supporting only one, so unsided
void accessChainPushComponent(Id component) { accessChain.component = component; }
// use accessChain and swizzle to store value
void accessChainStore(Id rvalue);
// use accessChain and swizzle to load an r-value
Id accessChainLoad(Decoration precision);
// get the direct pointer for an l-value
Id accessChainGetLValue();
void dump(std::vector<unsigned int>&) const;
protected:
Id findScalarConstant(OpCode typeClass, Id typeId, unsigned value) const;
Id findCompositeConstant(OpCode typeClass, std::vector<Id>& comps) const;
Id collapseAccessChain();
void simplifyAccessChainSwizzle();
void createAndSetNoPredecessorBlock(const char*);
void createBranch(Block* block);
void createMerge(OpCode, Block*, unsigned int control);
void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
void dumpInstructions(std::vector<unsigned int>&, const std::vector<Instruction*>&) const;
SourceLanguage source;
int sourceVersion;
std::vector<const char*> extensions;
AddressingModel addressModel;
MemoryModel memoryModel;
int builderNumber;
Module module;
Block* buildPoint;
Id uniqueId;
Function* mainFunction;
Block* stageExit;
AccessChain accessChain;
// special blocks of instructions for output
std::vector<Instruction*> imports;
std::vector<Instruction*> entryPoints;
std::vector<Instruction*> executionModes;
std::vector<Instruction*> names;
std::vector<Instruction*> lines;
std::vector<Instruction*> decorations;
std::vector<Instruction*> constantsTypesGlobals;
std::vector<Instruction*> externals;
// not output, internally used for quick & dirty canonical (unique) creation
std::vector<Instruction*> groupedConstants[OpConstant]; // all types appear before OpConstant
std::vector<Instruction*> groupedTypes[OpConstant];
// stack of switches
std::stack<Block*> switchMerges;
// Data that needs to be kept in order to properly handle loops.
struct Loop {
Block* header;
Block* merge;
Function* function;
};
// Our loop stack.
std::stack<Loop> loops;
}; // end Builder class
void MissingFunctionality(const char*);
void ValidationError(const char* error);
}; // end spv namespace
#endif // SpvBuilder_H
/*
** Copyright (c) 2014-2015 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
//
// Enumeration tokens for SPIR V.
//
#pragma once
#ifndef spirv_H
#define spirv_H
#ifdef __cplusplus
namespace spv{
#endif
const int MagicNumber = 0x07230203;
const int Version = 99;
typedef unsigned int Id;
const Id NoResult = 0;
const Id NoType = 0;
const unsigned int OpCodeMask = 0xFFFF;
const unsigned int WordCountShift = 16;
// Set of capabilities. Generally, something is assumed to be in core,
// if nothing else is said. So, these are used to identify when something
// requires a specific capability to be declared.
enum Capability {
CapMatrix,
CapShader,
CapGeom,
CapTess,
CapAddr,
CapLink,
CapKernel
};
// What language is the source code in? Note the OpSource instruction has a separate
// operand for the version number, this is just the language name. The GLSL
// compatibility profile will be indicated by using an OpSourceExtension string.
enum SourceLanguage {
LangUnknown,
LangESSL,
LangGLSL,
LangOpenCL,
LangCount // guard for validation, "default:" statements, etc.
};
// Used per entry point to communicate the "stage" or other model of
// execution used by that entry point.
// See OpEntryPoint.
enum ExecutionModel {
ModelVertex,
ModelTessellationControl,
ModelTessellationEvaluation,
ModelGeometry,
ModelFragment,
ModelGLCompute,
ModelKernel,
ModelCount // guard for validation, "default:" statements, etc.
};
// Used as an argument to OpMemoryModel
enum AddressingModel {
AddressingLogical,
AddressingPhysical32,
AddressingPhysical64,
AddressingCount // guard for validation, "default:" statements, etc.
};
// Used as an argment to OpMemoryModel
enum MemoryModel {
MemorySimple,
MemoryGLSL450,
MemoryOCL12,
MemoryOCL20,
MemoryOCL21,
MemoryCount // guard for validation, "default:" statements, etc.
};
// Used per entry point to communicate modes related to input, output, and execution.
// See OpExecutionMode.
enum ExecutionMode {
ExecutionInvocations,
ExecutionSpacingEqual,
ExecutionSpacingFractionalEven,
ExecutionSpacingFractionalOdd,
ExecutionVertexOrderCw,
ExecutionVertexOrderCcw,
ExecutionPixelCenterInteger,
ExecutionOriginUpperLeft,
ExecutionEarlyFragmentTests,
ExecutionPointMode,
ExecutionXfb,
ExecutionDepthReplacing,
ExecutionDepthAny,
ExecutionDepthGreater,
ExecutionDepthLess,
ExecutionDepthUnchanged,
ExecutionLocalSize,
ExecutionLocalSizeHint,
ExecutionInputPoints,
ExecutionInputLines,
ExecutionInputLinesAdjacency,
ExecutionInputTriangles,
ExecutionInputTrianglesAdjacency,
ExecutionInputQuads,
ExecutionInputIsolines,
ExecutionOutputVertices,
ExecutionOutputPoints,
ExecutionOutputLineStrip,
ExecutionOutputTriangleStrip,
ExecutionVecTypeHint,
ExecutionContractionOff,
ExecutionModeCount // guard for validation, "default:" statements, etc.
};
enum StorageClass {
StorageConstantUniform,
StorageInput,
StorageUniform,
StorageOutput,
StorageWorkgroupLocal,
StorageWorkgroupGlobal,
StoragePrivateGlobal,
StorageFunction,
StorageGeneric,
StoragePrivate,
StorageAtomicCounter,
StorageCount // guard for validation, "default:" statements, etc.
};
// Dimensionalities currently used for sampling.
// See TypeSampler in TypeClass.
enum Dimensionality {
Dim1D,
Dim2D,
Dim3D,
DimCube,
DimRect,
DimBuffer,
DimCount // guard for validation, "default:" statements, etc.
};
// Sampler addressing mode.
enum SamplerAddressingMode {
SamplerAddressingNone = 0,
SamplerAddressingClampToEdge = 2,
SamplerAddressingClamp = 4,
SamplerAddressingRepeat = 6,
SamplerAddressingRepeatMirrored = 8,
SamplerAddressingModeLast,
};
// Sampler filter mode.
enum SamplerFilterMode {
SamplerFilterNearest = 0x10,
SamplerFilterLinear = 0x20,
SamplerFilterModeLast,
};
// FP Fast Math Mode.
enum FPFastMath {
FPFastMathNNan = 0, // assume parameters and result are not NaN.
FPFastMathNInf = 0x02, // assume parameters and result are not +/- Inf.
FPFastMathNSZ = 0x04, // treat the sign of a zero parameter or result as insignificant.
FPFastMathARcp = 0x08, // allow the usage of reciprocal rather than perform a division.
FPFastMathFast = 0x10, // allow Algebraic transformations according to real number associative and distibutive algebra. This flag implies all the others.
FPFastMathLast,
};
// FP Fast Math Mode.
enum FPRoundingMode {
FPRoundRTE, // round to nearest even.
FPRoundRTZ, // round towards zero.
FPRoundRTP, // round towards positive infinity.
FPRoundRTN, // round towards negative infinity.
FPRoundLast,
};
// Global identifier linkage types (by default the linkage type of global identifiers is private. This means that they are only accessible to objects inside the module.)
enum LinkageType {
LinkageExport, // accessible by objects in other modules as well.
LinkageImport, // a forward declaration to a global identifier that exists in another module.
LinkageLast,
};
// Access Qualifiers for OpenCL pipes and images
enum AccessQualifier {
AccessQualReadOnly,
AccessQualWriteOnly,
AccessQualReadWrite,
AccessQualLast,
};
// Function argument attributes
enum FunctionParameterAttribute {
FuncParamAttrZext, // value should be zero extended if needed
FuncParamAttrSext, // value should be sign extended if needed
FuncParamAttrByval, // only valid for pointer parameters (not for ret value), this indicates that the pointer parameter should really be passed by value to the function.
FuncParamAttrSret, // indicates that the pointer parameter specifies the address of a structure that is the return value of the function in the source program. only applicable to the first parameter
FuncParamAttrNoAlias,
FuncParamAttrNoCapture,
FuncParamAttrSVM,
FuncParamAttrNoWrite,
FuncParamAttrNoReadWrite,
FuncParamAttrLast, // guard for validation, "default:" statements, etc.
};
// Extra forms of "qualification" to add as needed. See OpDecorate.
enum Decoration {
// For legacy ES precision qualifiers; newer language
// designs can use the "num-bits" feature in TypeClass.
// The precision qualifiers may be decorated on type <id>s or instruction <id>s.
DecPrecisionLow,
DecPrecisionMedium,
DecPrecisionHigh,
DecBlock, // basic in/out/uniform block, applied only to types of TypeStruct
DecBufferBlock, // shader storage buffer block
DecRowMajor,
DecColMajor,
DecGLSLShared,
DecGLSLStd140,
DecGLSLStd430,
DecGLSLPacked,
DecSmooth,
DecNoperspective,
DecFlat,
DecPatch,
DecCentroid,
DecSample,
DecInvariant,
DecRestrict,
DecAliased,
DecVolatile,
DecConstant,
DecCoherent,
DecNonwritable,
DecNonreadable,
DecUniform,
DecNoStaticUse,
DecCPacked,
DecFPSaturatedConv,
// these all take one additional operand
DecStream,
DecLocation,
DecComponent,
DecIndex,
DecBinding,
DecDescriptorSet,
DecOffset,
DecAlignment,
DecXfbBuffer,
DecStride,
DecBuiltIn,
DecFuncParamAttr,
DecFPRoundingMode,
DecFPFastMathMode,
DecLinkageType,
DecSpecId,
DecCount // guard for validation, "default:" statements, etc.
};
enum BuiltIn {
BuiltInPosition,
BuiltInPointSize,
BuiltInClipVertex,
BuiltInClipDistance,
BuiltInCullDistance,
BuiltInVertexId,
BuiltInInstanceId,
BuiltInPrimitiveId,
BuiltInInvocationId,
BuiltInLayer,
BuiltInViewportIndex,
BuiltInTessLevelOuter,
BuiltInTessLevelInner,
BuiltInTessCoord,
BuiltInPatchVertices,
BuiltInFragCoord,
BuiltInPointCoord,
BuiltInFrontFacing,
BuiltInSampleId,
BuiltInSamplePosition,
BuiltInSampleMask,
BuiltInFragColor,
BuiltInFragDepth,
BuiltInHelperInvocation,
// OpenGL compute stage, OpenCL work item built-ins
BuiltInNumWorkgroups, // number of work-groups that will execute a kernel
BuiltInWorkgroupSize, // OpenCL number of local work-items
BuiltInWorkgroupId, // OpenCL work group id
BuiltInLocalInvocationId, // OpenCL local work item id (decorates a vector3 i32/i64)
BuiltInGlobalInvocationId, // OpenCL global work item id (decorates a vector3 i32/i64)
BuiltInLocalInvocationIndex, // not in use in OpenCL
BuiltInWorkDim, // OpenCL number of dimensions in use (decorates a scalar i32/i64)
BuiltInGlobalSize, // OpenCL number of global work items, per dimension (decorates a vector3 i32/i64)
BuiltInEnqueuedWorkgroupSize, // OpenCL 2.0 only, get local size
BuiltInGlobalOffset, // OpenCL offset values specified global_work_offset
BuiltInGlobalLinearId, // OpenCL 2.0 only, work items 1-dimensional global ID.
BuiltInWorkgroupLinearId, // OpenCL 2.0 only work items 1-dimensional local ID.
// OpenCL 2.0 subgroups
BuiltInSubgroupSize, // Returns the number of work-items in the subgroup
BuiltInSubgroupMaxSize, // Returns the maximum size of a subgroup within the dispatch
BuiltInNumSubgroups, // Returns the maximum size of a subgroup within the dispatch
BuiltInNumEnqueuedSubgroups, // Returns the maximum size of a subgroup within the dispatch
BuiltInSubgroupId, //
BuiltInSubgroupLocalInvocationId, // Returns the unique work-item ID within the current subgroup
BuiltInCount // guard for validation, "default:" statements, etc.
};
enum SelectControl {
SelectControlNone,
SelectControlFlatten,
SelectControlDontFlatten,
SelectControlCount, // guard for validation, "default:" statements, etc.
};
enum LoopControl {
LoopControlNone,
LoopControlUnroll,
LoopControlDontUnroll,
LoopControlCount,
};
enum FunctionControlMask {
FunctionControlNone = 0x0,
FunctionControlInline = 0x1,
FunctionControlDontInline = 0x2,
FunctionControlPure = 0x4,
FunctionControlConst = 0x8,
FunctionControlCount = 4,
};
enum MemorySemanticsMask {
MemorySemanticsRelaxed = 0x0001,
MemorySemanticsSequentiallyConsistent = 0x0002,
MemorySemanticsAcquire = 0x0004,
MemorySemanticsRelease = 0x0008,
MemorySemanticsUniform = 0x0010,
MemorySemanticsSubgroup = 0x0020,
MemorySemanticsWorkgroupLocal = 0x0040,
MemorySemanticsWorkgroupGlobal = 0x0080,
MemorySemanticsAtomicCounter = 0x0100,
MemorySemanticsImage = 0x0200,
MemorySemanticsAllMemory = 0x03FF,
MemorySemanticsCount = 10
};
enum MemoryAccessMask {
MemoryAccessVolatile = 0x0001,
MemoryAccessAligned = 0x0002,
MemoryAccessCount = 2
};
enum ExecutionScope {
ExecutionScopeCrossDevice,
ExecutionScopeDevice,
ExecutionScopeWorkgroup,
ExecutionScopeSubgroup,
ExecutionScopeCount // guard for validation, "default:" statements, etc.
};
enum GroupOperation {
GroupOpReduce,
GroupOpInclusiveScan,
GroupOpExclusiveScan,
GroupOpCount
};
enum KernelEnqueueFlags {
EnqFlagNoWait,
EnqFlagWaitKernel,
EnqFlagWaitWaitWorgGroup,
EnqFlagCount
};
enum KernelProfilingInfo {
ProfInfoCmdExecTime = 0x01,
ProfilingInfoCount = 1
};
enum OpCode {
OpNop = 0, // Not used.
OpSource,
OpSourceExtension,
OpExtension,
OpExtInstImport,
OpMemoryModel,
OpEntryPoint,
OpExecutionMode,
OpTypeVoid,
OpTypeBool,
OpTypeInt,
OpTypeFloat,
OpTypeVector,
OpTypeMatrix,
OpTypeSampler,
OpTypeFilter,
OpTypeArray,
OpTypeRuntimeArray,
OpTypeStruct,
OpTypeOpaque,
OpTypePointer,
OpTypeFunction,
OpTypeEvent,
OpTypeDeviceEvent,
OpTypeReserveId,
OpTypeQueue,
OpTypePipe,
OpConstantTrue,
OpConstantFalse,
OpConstant,
OpConstantComposite,
OpConstantSampler,
OpConstantNullPointer,
OpConstantNullObject,
OpSpecConstantTrue,
OpSpecConstantFalse,
OpSpecConstant,
OpSpecConstantComposite,
OpVariable,
OpVariableArray,
OpFunction,
OpFunctionParameter,
OpFunctionEnd,
OpFunctionCall,
OpExtInst,
OpUndef,
OpLoad,
OpStore,
OpPhi,
OpDecorationGroup,
OpDecorate,
OpMemberDecorate,
OpGroupDecorate,
OpGroupMemberDecorate,
OpName,
OpMemberName,
OpString,
OpLine,
OpVectorExtractDynamic,
OpVectorInsertDynamic,
OpVectorShuffle,
OpCompositeConstruct,
OpCompositeExtract,
OpCompositeInsert,
OpCopyObject,
OpCopyMemory,
OpCopyMemorySized,
OpSampler,
OpTextureSample,
OpTextureSampleDref,
OpTextureSampleLod,
OpTextureSampleProj,
OpTextureSampleGrad,
OpTextureSampleOffset,
OpTextureSampleProjLod,
OpTextureSampleProjGrad,
OpTextureSampleLodOffset,
OpTextureSampleProjOffset,
OpTextureSampleGradOffset,
OpTextureSampleProjLodOffset,
OpTextureSampleProjGradOffset,
OpTextureFetchTexel,
OpTextureFetchTexelOffset,
OpTextureFetchSample,
OpTextureFetchBuffer,
OpTextureGather,
OpTextureGatherOffset,
OpTextureGatherOffsets,
OpTextureQuerySizeLod,
OpTextureQuerySize,
OpTextureQueryLod,
OpTextureQueryLevels,
OpTextureQuerySamples,
OpAccessChain,
OpInBoundsAccessChain,
OpSNegate,
OpFNegate,
OpNot,
OpAny,
OpAll,
OpConvertFToU,
OpConvertFToS,
OpConvertSToF,
OpConvertUToF,
OpUConvert,
OpSConvert,
OpFConvert,
OpConvertPtrToU,
OpConvertUToPtr,
OpPtrCastToGeneric, // cast a pointer storage class to be in storage generic
OpGenericCastToPtr, // cast a pointer in the generic storage class generic to another storage class
OpBitcast,
OpTranspose,
OpIsNan,
OpIsInf,
OpIsFinite,
OpIsNormal,
OpSignBitSet,
OpLessOrGreater,
OpOrdered,
OpUnordered,
OpArrayLength,
OpIAdd,
OpFAdd,
OpISub,
OpFSub,
OpIMul,
OpFMul,
OpUDiv,
OpSDiv,
OpFDiv,
OpUMod,
OpSRem,
OpSMod,
OpFRem,
OpFMod,
OpVectorTimesScalar,
OpMatrixTimesScalar,
OpVectorTimesMatrix,
OpMatrixTimesVector,
OpMatrixTimesMatrix,
OpOuterProduct,
OpDot,
OpShiftRightLogical,
OpShiftRightArithmetic,
OpShiftLeftLogical,
OpLogicalOr,
OpLogicalXor,
OpLogicalAnd,
OpBitwiseOr,
OpBitwiseXor,
OpBitwiseAnd,
OpSelect,
OpIEqual,
OpFOrdEqual,
OpFUnordEqual,
OpINotEqual,
OpFOrdNotEqual,
OpFUnordNotEqual,
OpULessThan,
OpSLessThan,
OpFOrdLessThan,
OpFUnordLessThan,
OpUGreaterThan,
OpSGreaterThan,
OpFOrdGreaterThan,
OpFUnordGreaterThan,
OpULessThanEqual,
OpSLessThanEqual,
OpFOrdLessThanEqual,
OpFUnordLessThanEqual,
OpUGreaterThanEqual,
OpSGreaterThanEqual,
OpFOrdGreaterThanEqual,
OpFUnordGreaterThanEqual,
OpDPdx,
OpDPdy,
OpFwidth,
OpDPdxFine,
OpDPdyFine,
OpFwidthFine,
OpDPdxCoarse,
OpDPdyCoarse,
OpFwidthCoarse,
OpEmitVertex,
OpEndPrimitive,
OpEmitStreamVertex,
OpEndStreamPrimitive,
OpControlBarrier,
OpMemoryBarrier,
OpImagePointer,
OpAtomicInit,
OpAtomicLoad,
OpAtomicStore,
OpAtomicExchange,
OpAtomicCompareExchange,
OpAtomicCompareExchangeWeak,
OpAtomicIIncrement,
OpAtomicIDecrement,
OpAtomicIAdd,
OpAtomicISub,
OpAtomicUMin,
OpAtomicUMax,
OpAtomicAnd,
OpAtomicOr,
OpAtomicXor,
OpLoopMerge,
OpSelectionMerge,
OpLabel,
OpBranch,
OpBranchConditional,
OpSwitch,
OpKill,
OpReturn,
OpReturnValue,
OpUnreachable,
OpLifetimeStart,
OpLifetimeStop,
OpCompileFlag,
OpAsyncGroupCopy,
OpWaitGroupEvents,
OpGroupAll,
OpGroupAny,
OpGroupBroadcast,
OpGroupIAdd,
OpGroupFAdd,
OpGroupFMin,
OpGroupUMin,
OpGroupSMin,
OpGroupFMax,
OpGroupUMax,
OpGroupSMax,
OpGenericCastToPtrExplicit,
OpGenericPtrMemSemantics,
OpReadPipe,
OpWritePipe,
OpReservedReadPipe,
OpReservedWritePipe,
OpReserveReadPipePackets,
OpReserveWritePipePackets,
OpCommitReadPipe,
OpCommitWritePipe,
OpIsValidReserveId,
OpGetNumPipePackets,
OpGetMaxPipePackets,
OpGroupReserveReadPipePackets,
OpGroupReserveWritePipePackets,
OpGroupCommitReadPipe,
OpGroupCommitWritePipe,
OpEnqueueMarker,
OpEnqueueKernel,
OpGetKernelNDrangeSubGroupCount,
OpGetKernelNDrangeMaxSubGroupSize,
OpGetKernelWorkGroupSize,
OpGetKernelPreferredWorkGroupSizeMultiple,
OpRetainEvent,
OpReleaseEvent,
OpCreateUserEvent,
OpIsValidEvent,
OpSetUserEventStatus,
OpCaptureEventProfilingInfo,
OpGetDefaultQueue,
OpBuildNDRange,
OpCount // guard for validation, "default:" statements, etc.
};
#ifdef __cplusplus
}; // end namespace spv
#endif
#endif // spirv_H
//
//Copyright (C) 2014 LunarG, Inc.
//
//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.
//
// Author: John Kessenich, LunarG
//
// SPIRV-IR
//
// Simple in-memory representation (IR) of SPIRV. Just for holding
// Each function's CFG of blocks. Has this hierarchy:
// - Module, which is a list of
// - Function, which is a list of
// - Block, which is a list of
// - Instruction
//
#pragma once
#ifndef spvIR_H
#define spvIR_H
#include "spirv.h"
#include <vector>
#include <iostream>
namespace spv {
class Function;
class Module;
//
// SPIR-V IR instruction.
//
class Instruction {
public:
Instruction(Id resultId, Id typeId, OpCode opCode) : resultId(resultId), typeId(typeId), opCode(opCode), string(0) { }
explicit Instruction(OpCode opCode) : resultId(NoResult), typeId(NoType), opCode(opCode), string(0) { }
virtual ~Instruction()
{
delete string;
}
void addIdOperand(Id id) { operands.push_back(id); }
void addImmediateOperand(unsigned int immediate) { operands.push_back(immediate); }
void addStringOperand(const char* str)
{
string = new std::vector<unsigned int>;
unsigned int word;
char* wordString = (char*)&word;
char* wordPtr = wordString;
int charCount = 0;
char c;
do {
c = *(str++);
*(wordPtr++) = c;
++charCount;
if (charCount == 4) {
string->push_back(word);
wordPtr = wordString;
charCount = 0;
}
} while (c != 0);
// deal with partial last word
if (charCount > 0) {
// pad with 0s
for (; charCount < 4; ++charCount)
*(wordPtr++) = 0;
string->push_back(word);
}
originalString = str;
}
OpCode getOpCode() const { return opCode; }
int getNumOperands() const { return operands.size(); }
Id getResultId() const { return resultId; }
Id getTypeId() const { return typeId; }
Id getIdOperand(int op) const { return operands[op]; }
unsigned int getImmediateOperand(int op) const { return operands[op]; }
const char* getStringOperand() const { return originalString.c_str(); }
// Write out the binary form.
void dump(std::vector<unsigned int>& out) const
{
// Compute the wordCount
unsigned int wordCount = 1;
if (typeId)
++wordCount;
if (resultId)
++wordCount;
wordCount += operands.size();
if (string)
wordCount += string->size();
// Write out the beginning of the instruction
out.push_back(((wordCount) << WordCountShift) | opCode);
if (typeId)
out.push_back(typeId);
if (resultId)
out.push_back(resultId);
// Write out the operands
for (int op = 0; op < (int)operands.size(); ++op)
out.push_back(operands[op]);
if (string)
for (int op = 0; op < (int)string->size(); ++op)
out.push_back((*string)[op]);
}
protected:
Instruction(const Instruction&);
Id resultId;
Id typeId;
OpCode opCode;
std::vector<Id> operands;
std::vector<unsigned int>* string; // usually non-existent
std::string originalString; // could be optimized away; convenience for getting string operand
};
//
// SPIR-V IR block.
//
class Block {
public:
// Setting insert to true indicates to add this new block
// to the end of the parent function.
Block(Id id, Function& parent);
virtual ~Block()
{
// TODO: free instructions
}
Id getId() { return instructions.front()->getResultId(); }
Function& getParent() const { return parent; }
void addInstruction(Instruction* inst);
void addPredecessor(Block* pred) { predecessors.push_back(pred); }
void addLocalVariable(Instruction* inst) { localVariables.push_back(inst); }
int getNumPredecessors() const { return (int)predecessors.size(); }
bool isTerminated() const
{
switch (instructions.back()->getOpCode()) {
case OpBranch:
case OpBranchConditional:
case OpSwitch:
case OpKill:
case OpReturn:
case OpReturnValue:
return true;
default:
return false;
}
}
void dump(std::vector<unsigned int>& out) const
{
instructions[0]->dump(out);
for (int i = 0; i < (int)localVariables.size(); ++i)
localVariables[i]->dump(out);
for (int i = 1; i < (int)instructions.size(); ++i)
instructions[i]->dump(out);
}
protected:
Block(const Block&);
// To enforce keeping parent and ownership in sync:
friend Function;
std::vector<Instruction*> instructions;
std::vector<Block*> predecessors;
std::vector<Instruction*> localVariables;
Function& parent;
};
//
// SPIR-V IR Function.
//
class Function {
public:
Function(Id id, Id resultType, Id functionType, Id firstParam, Module& parent);
virtual ~Function()
{
for (int i = 0; i < (int)parameterInstructions.size(); ++i)
delete parameterInstructions[i];
for (int i = 0; i < (int)blocks.size(); ++i)
delete blocks[i];
}
Id getId() const { return functionInstruction.getResultId(); }
Id getParamId(int p) { return parameterInstructions[p]->getResultId(); }
void addBlock(Block* block) { blocks.push_back(block); }
void popBlock(Block* block) { assert(blocks.back() == block); blocks.pop_back(); }
Module& getParent() const { return parent; }
Block* getEntryBlock() const { return blocks.front(); }
Block* getLastBlock() const { return blocks.back(); }
void addLocalVariable(Instruction* inst);
Id getReturnType() const { return functionInstruction.getTypeId(); }
void dump(std::vector<unsigned int>& out) const
{
// OpFunction
functionInstruction.dump(out);
// OpFunctionParameter
for (int p = 0; p < (int)parameterInstructions.size(); ++p)
parameterInstructions[p]->dump(out);
// Blocks
for (int b = 0; b < (int)blocks.size(); ++b)
blocks[b]->dump(out);
Instruction end(0, 0, OpFunctionEnd);
end.dump(out);
}
protected:
Function(const Function&);
Module& parent;
Instruction functionInstruction;
std::vector<Instruction*> parameterInstructions;
std::vector<Block*> blocks;
};
//
// SPIR-V IR Module.
//
class Module {
public:
Module() {}
virtual ~Module()
{
// TODO delete things
}
void addFunction(Function *fun) { functions.push_back(fun); }
void mapInstruction(Instruction *instruction)
{
spv::Id resultId = instruction->getResultId();
// map the instruction's result id
if (resultId >= idToInstruction.size())
idToInstruction.resize(resultId + 16);
idToInstruction[resultId] = instruction;
}
Instruction* getInstruction(Id id) const { return idToInstruction[id]; }
spv::Id getTypeId(Id resultId) const { return idToInstruction[resultId]->getTypeId(); }
StorageClass getStorageClass(Id typeId) const { return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0); }
void dump(std::vector<unsigned int>& out) const
{
for (int f = 0; f < (int)functions.size(); ++f)
functions[f]->dump(out);
}
protected:
Module(const Module&);
std::vector<Function*> functions;
// map from result id to instruction having that result id
std::vector<Instruction*> idToInstruction;
// map from a result id to its type id
};
//
// Implementation (it's here due to circular type definitions).
//
// Add both
// - the OpFunction instruction
// - all the OpFunctionParameter instructions
__inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, Module& parent)
: parent(parent), functionInstruction(id, resultType, OpFunction)
{
// OpFunction
functionInstruction.addImmediateOperand(FunctionControlNone);
functionInstruction.addIdOperand(functionType);
parent.mapInstruction(&functionInstruction);
parent.addFunction(this);
// OpFunctionParameter
Instruction* typeInst = parent.getInstruction(functionType);
int numParams = typeInst->getNumOperands() - 1;
for (int p = 0; p < numParams; ++p) {
Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), OpFunctionParameter);
parent.mapInstruction(param);
parameterInstructions.push_back(param);
}
}
__inline void Function::addLocalVariable(Instruction* inst)
{
blocks[0]->addLocalVariable(inst);
parent.mapInstruction(inst);
}
__inline Block::Block(Id id, Function& parent) : parent(parent)
{
instructions.push_back(new Instruction(id, NoType, OpLabel));
}
__inline void Block::addInstruction(Instruction* inst)
{
instructions.push_back(inst);
if (inst->getResultId())
parent.getParent().mapInstruction(inst);
}
}; // end spv namespace
#endif // spvIR_H
...@@ -17,7 +17,7 @@ set(LIBRARIES ...@@ -17,7 +17,7 @@ set(LIBRARIES
glslang glslang
OGLCompiler OGLCompiler
OSDependent OSDependent
BIL) SPIRV)
if(WIN32) if(WIN32)
set(LIBRARIES ${LIBRARIES} psapi) set(LIBRARIES ${LIBRARIES} psapi)
......
...@@ -40,8 +40,8 @@ ...@@ -40,8 +40,8 @@
#include "Worklist.h" #include "Worklist.h"
#include "./../glslang/Include/ShHandle.h" #include "./../glslang/Include/ShHandle.h"
#include "./../glslang/Public/ShaderLang.h" #include "./../glslang/Public/ShaderLang.h"
#include "../BIL/GlslangToBil.h" #include "../SPIRV/GlslangToSpv.h"
#include "../BIL/GLSL450Lib.h" #include "../SPIRV/GLSL450Lib.h"
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <math.h> #include <math.h>
...@@ -66,7 +66,7 @@ enum TOptions { ...@@ -66,7 +66,7 @@ enum TOptions {
EOptionDumpReflection = 0x100, EOptionDumpReflection = 0x100,
EOptionSuppressWarnings = 0x200, EOptionSuppressWarnings = 0x200,
EOptionDumpVersions = 0x400, EOptionDumpVersions = 0x400,
EOptionBil = 0x800, EOptionSpv = 0x800,
EOptionDefaultDesktop = 0x1000, EOptionDefaultDesktop = 0x1000,
}; };
...@@ -479,8 +479,8 @@ bool ProcessArguments(int argc, char* argv[]) ...@@ -479,8 +479,8 @@ bool ProcessArguments(int argc, char* argv[])
Work[argc] = 0; Work[argc] = 0;
if (argv[0][0] == '-') { if (argv[0][0] == '-') {
switch (argv[0][1]) { switch (argv[0][1]) {
case 'b': case 'V':
Options |= EOptionBil; Options |= EOptionSpv;
Options |= EOptionLinkProgram; Options |= EOptionLinkProgram;
break; break;
case 'c': case 'c':
...@@ -634,14 +634,14 @@ void CompileAndLinkShaders() ...@@ -634,14 +634,14 @@ void CompileAndLinkShaders()
program.dumpReflection(); program.dumpReflection();
} }
if (Options & EOptionBil) { if (Options & EOptionSpv) {
if (CompileFailed || LinkFailed) if (CompileFailed || LinkFailed)
printf("Bil is not generated for failed compile or link\n"); printf("SPIRV is not generated for failed compile or link\n");
else { else {
for (int stage = 0; stage < EShLangCount; ++stage) { for (int stage = 0; stage < EShLangCount; ++stage) {
if (program.getIntermediate((EShLanguage)stage)) { if (program.getIntermediate((EShLanguage)stage)) {
std::vector<unsigned int> bil; std::vector<unsigned int> spirv;
glslang::GlslangToBil(*program.getIntermediate((EShLanguage)stage), bil); glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv);
const char* name; const char* name;
switch (stage) { switch (stage) {
case EShLangVertex: name = "vert"; break; case EShLangVertex: name = "vert"; break;
...@@ -652,7 +652,7 @@ void CompileAndLinkShaders() ...@@ -652,7 +652,7 @@ void CompileAndLinkShaders()
case EShLangCompute: name = "comp"; break; case EShLangCompute: name = "comp"; break;
default: name = "unknown"; break; default: name = "unknown"; break;
} }
glslang::OutputBil(bil, name); glslang::OutputSpv(spirv, name);
} }
} }
} }
...@@ -839,7 +839,7 @@ void usage() ...@@ -839,7 +839,7 @@ void usage()
{ {
printf("Usage: glslangValidator [option]... [file]...\n" printf("Usage: glslangValidator [option]... [file]...\n"
"\n" "\n"
"Where: each 'file' ends in\n" "Where: each 'file' ends in .<stage>, where <stage> is one of\n"
" .conf to provide an optional config file that replaces the default configuration\n" " .conf to provide an optional config file that replaces the default configuration\n"
" (see -c option below for generating a template)\n" " (see -c option below for generating a template)\n"
" .vert for a vertex shader\n" " .vert for a vertex shader\n"
...@@ -853,7 +853,7 @@ void usage() ...@@ -853,7 +853,7 @@ void usage()
"\n" "\n"
"To get other information, use one of the following options:\n" "To get other information, use one of the following options:\n"
"(Each option must be specified separately, but can go anywhere in the command line.)\n" "(Each option must be specified separately, but can go anywhere in the command line.)\n"
" -b create BIL in file <stage>.bil\n" " -V create SPIR-V in file <stage>.spv\n"
" -c configuration dump; use to create default configuration file (redirect to a .conf file)\n" " -c configuration dump; use to create default configuration file (redirect to a .conf file)\n"
" -d default to desktop (#version 110) when there is no version in the shader (default is ES version 100)\n" " -d default to desktop (#version 110) when there is no version in the shader (default is ES version 100)\n"
" -i intermediate tree (glslang AST) is printed out\n" " -i intermediate tree (glslang AST) is printed out\n"
......
...@@ -482,6 +482,7 @@ public: ...@@ -482,6 +482,7 @@ public:
layoutLocation = layoutLocationEnd; layoutLocation = layoutLocationEnd;
layoutComponent = layoutComponentEnd; layoutComponent = layoutComponentEnd;
layoutSet = layoutSetEnd;
layoutBinding = layoutBindingEnd; layoutBinding = layoutBindingEnd;
layoutIndex = layoutIndexEnd; layoutIndex = layoutIndexEnd;
...@@ -513,6 +514,9 @@ public: ...@@ -513,6 +514,9 @@ public:
unsigned int layoutComponent : 3; unsigned int layoutComponent : 3;
static const unsigned int layoutComponentEnd = 4; static const unsigned int layoutComponentEnd = 4;
unsigned int layoutSet : 7;
static const unsigned int layoutSetEnd = 0x3F;
unsigned int layoutBinding : 8; unsigned int layoutBinding : 8;
static const unsigned int layoutBindingEnd = 0xFF; static const unsigned int layoutBindingEnd = 0xFF;
...@@ -575,6 +579,10 @@ public: ...@@ -575,6 +579,10 @@ public:
{ {
return layoutIndex != layoutIndexEnd; return layoutIndex != layoutIndexEnd;
} }
bool hasSet() const
{
return layoutSet != layoutSetEnd;
}
bool hasBinding() const bool hasBinding() const
{ {
return layoutBinding != layoutBindingEnd; return layoutBinding != layoutBindingEnd;
...@@ -1201,6 +1209,8 @@ public: ...@@ -1201,6 +1209,8 @@ public:
if (qualifier.hasIndex()) if (qualifier.hasIndex())
p += snprintf(p, end - p, "index=%d ", qualifier.layoutIndex); p += snprintf(p, end - p, "index=%d ", qualifier.layoutIndex);
} }
if (qualifier.hasSet())
p += snprintf(p, end - p, "set=%d ", qualifier.layoutSet);
if (qualifier.hasBinding()) if (qualifier.hasBinding())
p += snprintf(p, end - p, "binding=%d ", qualifier.layoutBinding); p += snprintf(p, end - p, "binding=%d ", qualifier.layoutBinding);
if (qualifier.hasStream()) if (qualifier.hasStream())
......
...@@ -3312,6 +3312,12 @@ void TParseContext::setLayoutQualifier(TSourceLoc loc, TPublicType& publicType, ...@@ -3312,6 +3312,12 @@ void TParseContext::setLayoutQualifier(TSourceLoc loc, TPublicType& publicType,
else else
publicType.qualifier.layoutLocation = value; publicType.qualifier.layoutLocation = value;
return; return;
} else if (id == "set") {
if ((unsigned int)value >= TQualifier::layoutSetEnd)
error(loc, "set is too large", id.c_str(), "");
else
publicType.qualifier.layoutSet = value;
return;
} else if (id == "binding") { } else if (id == "binding") {
profileRequires(loc, ~EEsProfile, 420, GL_ARB_shading_language_420pack, "binding"); profileRequires(loc, ~EEsProfile, 420, GL_ARB_shading_language_420pack, "binding");
profileRequires(loc, EEsProfile, 310, 0, "binding"); profileRequires(loc, EEsProfile, 310, 0, "binding");
...@@ -3476,6 +3482,8 @@ void TParseContext::mergeObjectLayoutQualifiers(TSourceLoc loc, TQualifier& dst, ...@@ -3476,6 +3482,8 @@ void TParseContext::mergeObjectLayoutQualifiers(TSourceLoc loc, TQualifier& dst,
if (src.hasOffset()) if (src.hasOffset())
dst.layoutOffset = src.layoutOffset; dst.layoutOffset = src.layoutOffset;
if (src.hasSet())
dst.layoutSet = src.layoutSet;
if (src.layoutBinding != TQualifier::layoutBindingEnd) if (src.layoutBinding != TQualifier::layoutBindingEnd)
dst.layoutBinding = src.layoutBinding; dst.layoutBinding = src.layoutBinding;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment