From 0df0cdeeeb15b8a66b568b2fb8c85fad5b3f8825 Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Tue, 3 Mar 2015 17:09:43 +0000 Subject: [PATCH] glslangValidator: Add straightforward SPIR-V support (non-optimizing, ~3.x functionality). git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@30032 e7fa87d3-cd2b-0410-9028-fcbf551c1848 --- BIL/Bil.h | 6 - BIL/BilBuilder.cpp | 59 - BIL/BilBuilder.h | 56 - BIL/Bir.h | 49 - BIL/GLSL450Lib.h | 14 - BIL/GlslangToBil.cpp | 53 - CMakeLists.txt | 2 +- {BIL => SPIRV}/CMakeLists.txt | 16 +- SPIRV/GLSL450Lib.h | 212 ++ SPIRV/GlslangToSpv.cpp | 2589 ++++++++++++++++++++ BIL/GlslangToBil.h => SPIRV/GlslangToSpv.h | 5 +- SPIRV/SpvBuilder.cpp | 2011 +++++++++++++++ SPIRV/SpvBuilder.h | 519 ++++ SPIRV/spirv.h | 762 ++++++ SPIRV/spvIR.h | 348 +++ StandAlone/CMakeLists.txt | 2 +- StandAlone/StandAlone.cpp | 24 +- glslang/Include/Types.h | 10 + glslang/MachineIndependent/ParseHelper.cpp | 8 + 19 files changed, 6483 insertions(+), 262 deletions(-) delete mode 100644 BIL/Bil.h delete mode 100644 BIL/BilBuilder.cpp delete mode 100644 BIL/BilBuilder.h delete mode 100644 BIL/Bir.h delete mode 100644 BIL/GLSL450Lib.h delete mode 100644 BIL/GlslangToBil.cpp rename {BIL => SPIRV}/CMakeLists.txt (54%) create mode 100644 SPIRV/GLSL450Lib.h create mode 100644 SPIRV/GlslangToSpv.cpp rename BIL/GlslangToBil.h => SPIRV/GlslangToSpv.h (88%) create mode 100644 SPIRV/SpvBuilder.cpp create mode 100644 SPIRV/SpvBuilder.h create mode 100644 SPIRV/spirv.h create mode 100644 SPIRV/spvIR.h diff --git a/BIL/Bil.h b/BIL/Bil.h deleted file mode 100644 index f9ab2c4a..00000000 --- a/BIL/Bil.h +++ /dev/null @@ -1,6 +0,0 @@ - -#pragma once -#ifndef Bil_H -#define Bil_H - -#endif // Bil_H diff --git a/BIL/BilBuilder.cpp b/BIL/BilBuilder.cpp deleted file mode 100644 index d0eb21a2..00000000 --- a/BIL/BilBuilder.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// -//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 -#include - -#include "BilBuilder.h" - -#ifndef _WIN32 - #include -#endif - -namespace glbil { - -Builder::Builder() -{ -} - -Builder::~Builder() -{ -} - -void MissingFunctionality(const char* fun) -{ - printf("Missing functionality: %s\n", fun); -} - -}; // end glbil namespace diff --git a/BIL/BilBuilder.h b/BIL/BilBuilder.h deleted file mode 100644 index ba993d4b..00000000 --- a/BIL/BilBuilder.h +++ /dev/null @@ -1,56 +0,0 @@ -// -//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 - -namespace glbil { - -class Builder { -public: - Builder(); - virtual ~Builder(); -}; - -void MissingFunctionality(const char*); - -}; // end glbil namespace - -#endif // BilBuilder_H diff --git a/BIL/Bir.h b/BIL/Bir.h deleted file mode 100644 index eee9dece..00000000 --- a/BIL/Bir.h +++ /dev/null @@ -1,49 +0,0 @@ -// -//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 -#include - -namespace glbil { - - -}; // end glbil namespace - -#endif // Bir_H diff --git a/BIL/GLSL450Lib.h b/BIL/GLSL450Lib.h deleted file mode 100644 index 92ad0ede..00000000 --- a/BIL/GLSL450Lib.h +++ /dev/null @@ -1,14 +0,0 @@ -namespace GLSL_STD_450 { - -enum Entrypoints { - Round, - Count -}; - -extern const char* Names[Count]; - -inline void Initialize() -{ -} - -}; // end namespace GLSL_STD_450 diff --git a/BIL/GlslangToBil.cpp b/BIL/GlslangToBil.cpp deleted file mode 100644 index 89ab8ed4..00000000 --- a/BIL/GlslangToBil.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// -//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 bil) -{ -} - -void OutputBil(const std::vector& bil, const char* baseName) -{ -} - -}; // end namespace glslang diff --git a/CMakeLists.txt b/CMakeLists.txt index d852c480..51a8395f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,4 +16,4 @@ endif(WIN32) add_subdirectory(glslang) add_subdirectory(OGLCompilersDLL) add_subdirectory(StandAlone) -add_subdirectory(BIL) +add_subdirectory(SPIRV) diff --git a/BIL/CMakeLists.txt b/SPIRV/CMakeLists.txt similarity index 54% rename from BIL/CMakeLists.txt rename to SPIRV/CMakeLists.txt index 16959924..c723afbb 100644 --- a/BIL/CMakeLists.txt +++ b/SPIRV/CMakeLists.txt @@ -3,20 +3,20 @@ cmake_minimum_required(VERSION 2.8) include_directories(.. ${CMAKE_CURRENT_BINARY_DIR}) set(SOURCES - GlslangToBil.cpp - BilBuilder.cpp) + GlslangToSpv.cpp + SpvBuilder.cpp) set(HEADERS - Bil.h - GlslangToBil.h - BilBuilder.h - Bir.h) + spirv.h + GlslangToSpv.h + SpvBuilder.h + SpvIR.h) -add_library(BIL STATIC ${SOURCES} ${HEADERS}) +add_library(SPIRV STATIC ${SOURCES} ${HEADERS}) if(WIN32) source_group("Source" FILES ${SOURCES} ${HEADERS}) endif(WIN32) -install(TARGETS BIL +install(TARGETS SPIRV ARCHIVE DESTINATION lib) diff --git a/SPIRV/GLSL450Lib.h b/SPIRV/GLSL450Lib.h new file mode 100644 index 00000000..f32f143f --- /dev/null +++ b/SPIRV/GLSL450Lib.h @@ -0,0 +1,212 @@ +/* +** 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 diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp new file mode 100644 index 00000000..10e7b92e --- /dev/null +++ b/SPIRV/GlslangToSpv.cpp @@ -0,0 +1,2589 @@ +// +//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 +// +// Visit the nodes in the glslang intermediate tree representation to +// translate them to SPIR-V. +// + +#include "spirv.h" +#include "GlslangToSpv.h" +#include "SpvBuilder.h" +#include "GLSL450Lib.h" + +// Glslang includes +#include "glslang/MachineIndependent/localintermediate.h" +#include "glslang/MachineIndependent/SymbolTable.h" + +#include +#include +#include +#include +#include +#include + +namespace { + +const int GlslangMagic = 0x51a; + +// +// The main holder of information for translating glslang to SPIR-V. +// +// Derives from the AST walking base class. +// +class TGlslangToSpvTraverser : public glslang::TIntermTraverser { +public: + TGlslangToSpvTraverser(const glslang::TIntermediate*); + virtual ~TGlslangToSpvTraverser(); + + bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*); + bool visitBinary(glslang::TVisit, glslang::TIntermBinary*); + void visitConstantUnion(glslang::TIntermConstantUnion*); + bool visitSelection(glslang::TVisit, glslang::TIntermSelection*); + bool visitSwitch(glslang::TVisit, glslang::TIntermSwitch*); + void visitSymbol(glslang::TIntermSymbol* symbol); + bool visitUnary(glslang::TVisit, glslang::TIntermUnary*); + bool visitLoop(glslang::TVisit, glslang::TIntermLoop*); + bool visitBranch(glslang::TVisit visit, glslang::TIntermBranch*); + + void dumpSpv(std::vector& out) { builder.dump(out); } + +protected: + spv::Id createSpvVariable(const glslang::TIntermSymbol*); + spv::Id getSampledType(const glslang::TSampler&); + spv::Id convertGlslangToSpvType(const glslang::TType& type); + + bool isShaderEntrypoint(const glslang::TIntermAggregate* node); + void makeFunctions(const glslang::TIntermSequence&); + void makeGlobalInitializers(const glslang::TIntermSequence&); + void visitFunctions(const glslang::TIntermSequence&); + void handleFunctionEntry(const glslang::TIntermAggregate* node); + void translateArguments(const glslang::TIntermSequence& glslangArguments, std::vector& arguments); + spv::Id handleBuiltInFunctionCall(const glslang::TIntermAggregate*); + spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*); + + spv::Id createBinaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id left, spv::Id right, glslang::TBasicType typeProxy, bool reduceComparison = true); + spv::Id createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id operand, bool isFloat); + spv::Id createConversion(glslang::TOperator op, spv::Decoration precision, spv::Id destTypeId, spv::Id operand); + spv::Id makeSmearedConstant(spv::Id constant, int vectorSize); + spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector& operands, bool isUnsigned); + spv::Id createNoArgOperation(glslang::TOperator op); + spv::Id getSymbolId(const glslang::TIntermSymbol* node); + void addDecoration(spv::Id id, spv::Decoration dec); + void addMemberDecoration(spv::Id id, int member, spv::Decoration dec); + spv::Id createSpvConstant(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst); + + spv::Function* shaderEntry; + int sequenceDepth; + + // There is a 1:1 mapping between a spv builder and a module; this is thread safe + spv::Builder builder; + bool inMain; + bool mainTerminated; + bool linkageOnly; + const glslang::TIntermediate* glslangIntermediate; + spv::Id stdBuiltins; + + std::map symbolValues; + std::set constReadOnlyParameters; // set of formal function parameters that have glslang qualifier constReadOnly, so we know they are not local function "const" that are write-once + std::map functionMap; + std::map structMap; + std::map > memberRemapper; // for mapping glslang block indices to spv indices (e.g., due to hidden members) + std::stack breakForLoop; // false means break for switch + std::stack loopTerminal; // code from the last part of a for loop: for(...; ...; terminal), needed for e.g., continue }; +}; + +// +// Helper functions for translating glslang representations to SPIR-V enumerants. +// + +// Translate glslang language (stage) to SPIR-V execution model. +spv::ExecutionModel TranslateExecutionModel(EShLanguage stage) +{ + switch (stage) { + case EShLangVertex: return spv::ModelVertex; + case EShLangTessControl: return spv::ModelTessellationControl; + case EShLangTessEvaluation: return spv::ModelTessellationEvaluation; + case EShLangGeometry: return spv::ModelGeometry; + case EShLangFragment: return spv::ModelFragment; + case EShLangCompute: return spv::ModelGLCompute; + default: + spv::MissingFunctionality("GLSL stage"); + return spv::ModelFragment; + } +} + +// Translate glslang type to SPIR-V storage class. +spv::StorageClass TranslateStorageClass(const glslang::TType& type) +{ + if (type.getQualifier().isPipeInput()) + return spv::StorageInput; + else if (type.getQualifier().isPipeOutput()) + return spv::StorageOutput; + else if (type.getQualifier().isUniformOrBuffer()) { + if (type.getBasicType() == glslang::EbtBlock) + return spv::StorageUniform; + else + return spv::StorageConstantUniform; + // TODO: how are we distuingishing between default and non-default non-writable uniforms? Do default uniforms even exist? + } else { + switch (type.getQualifier().storage) { + case glslang::EvqShared: return spv::StorageWorkgroupLocal; break; + case glslang::EvqGlobal: return spv::StoragePrivateGlobal; + case glslang::EvqConstReadOnly: return spv::StorageFunction; + case glslang::EvqTemporary: return spv::StorageFunction; + default: + spv::MissingFunctionality("unknown glslang storage class"); + return spv::StorageCount; + } + } +} + +// Translate glslang sampler type to SPIR-V dimensionality. +spv::Dimensionality TranslateDimensionality(const glslang::TSampler& sampler) +{ + switch (sampler.dim) { + case glslang::Esd1D: return spv::Dim1D; + case glslang::Esd2D: return spv::Dim2D; + case glslang::Esd3D: return spv::Dim3D; + case glslang::EsdCube: return spv::DimCube; + case glslang::EsdRect: return spv::DimRect; + case glslang::EsdBuffer: return spv::DimBuffer; + default: + spv::MissingFunctionality("unknown sampler dimension"); + return spv::Dim2D; + } +} + +// Translate glslang type to SPIR-V precision decorations. +spv::Decoration TranslatePrecisionDecoration(const glslang::TType& type) +{ + switch (type.getQualifier().precision) { + case glslang::EpqLow: return spv::DecPrecisionLow; + case glslang::EpqMedium: return spv::DecPrecisionMedium; + case glslang::EpqHigh: return spv::DecPrecisionHigh; + default: return spv::DecCount; + } +} + +// Translate glslang type to SPIR-V block decorations. +spv::Decoration TranslateBlockDecoration(const glslang::TType& type) +{ + if (type.getBasicType() == glslang::EbtBlock) { + switch (type.getQualifier().storage) { + case glslang::EvqUniform: return spv::DecBlock; + case glslang::EvqBuffer: return spv::DecBufferBlock; + case glslang::EvqVaryingIn: return spv::DecBlock; + case glslang::EvqVaryingOut: return spv::DecBlock; + default: + spv::MissingFunctionality("kind of block"); + break; + } + } + + return spv::DecCount; +} + +// Translate glslang type to SPIR-V layout decorations. +spv::Decoration TranslateLayoutDecoration(const glslang::TType& type) +{ + if (type.isMatrix()) { + switch (type.getQualifier().layoutMatrix) { + case glslang::ElmRowMajor: + return spv::DecRowMajor; + default: + return spv::DecColMajor; + } + } else { + switch (type.getBasicType()) { + default: + return spv::DecCount; + break; + case glslang::EbtBlock: + switch (type.getQualifier().storage) { + case glslang::EvqUniform: + case glslang::EvqBuffer: + switch (type.getQualifier().layoutPacking) { + case glslang::ElpShared: return spv::DecGLSLShared; + case glslang::ElpStd140: return spv::DecGLSLStd140; + case glslang::ElpStd430: return spv::DecGLSLStd430; + case glslang::ElpPacked: return spv::DecGLSLPacked; + default: + spv::MissingFunctionality("uniform block layout"); + return spv::DecGLSLShared; + } + case glslang::EvqVaryingIn: + case glslang::EvqVaryingOut: + if (type.getQualifier().layoutPacking != glslang::ElpNone) + spv::MissingFunctionality("in/out block layout"); + return spv::DecCount; + default: + spv::MissingFunctionality("block storage qualification"); + return spv::DecCount; + } + } + } +} + +// Translate glslang type to SPIR-V interpolation decorations. +spv::Decoration TranslateInterpolationDecoration(const glslang::TType& type) +{ + if (type.getQualifier().smooth) + return spv::DecSmooth; + if (type.getQualifier().nopersp) + return spv::DecNoperspective; + else if (type.getQualifier().patch) + return spv::DecPatch; + else if (type.getQualifier().flat) + return spv::DecFlat; + else if (type.getQualifier().centroid) + return spv::DecCentroid; + else if (type.getQualifier().sample) + return spv::DecSample; + else + return spv::DecCount; +} + +// If glslang type is invaraiant, return SPIR-V invariant decoration. +spv::Decoration TranslateInvariantDecoration(const glslang::TType& type) +{ + if (type.getQualifier().invariant) + return spv::DecInvariant; + else + return spv::DecCount; +} + +// Identify what SPIR-V built-in variable a symbol is. +// Return -1 if a symbol is not a built-in to decorate. +int TranslateBuiltInDecoration(const glslang::TIntermSymbol& symbol) +{ + switch (symbol.getQualifier().storage) { + case glslang::EvqVertexId: return spv::BuiltInVertexId; + case glslang::EvqInstanceId: return spv::BuiltInInstanceId; + case glslang::EvqPosition: return spv::BuiltInPosition; + case glslang::EvqPointSize: return spv::BuiltInPointSize; + case glslang::EvqClipVertex: return spv::BuiltInClipVertex; + case glslang::EvqFace: return spv::BuiltInFrontFacing; + case glslang::EvqFragCoord: return spv::BuiltInFragCoord; + case glslang::EvqPointCoord: return spv::BuiltInPointCoord; + case glslang::EvqFragColor: return spv::BuiltInFragColor; + case glslang::EvqFragDepth: return spv::BuiltInFragDepth; + default: + // TODO: built-ins not identified by storage qualifier + return -1; + } +} + +// Translate glslang built-in variable to SPIR-V built in. +spv::BuiltIn TranslateBuiltIn(const glslang::TIntermSymbol* node) +{ + const glslang::TString& name = node->getName(); + if (name.compare(0, 3, "gl_") != 0) + return spv::BuiltInCount; + + switch (node->getQualifier().storage) { + case glslang::EvqPosition: return spv::BuiltInPosition; + case glslang::EvqPointSize: return spv::BuiltInPointSize; + case glslang::EvqClipVertex: return spv::BuiltInClipVertex; + case glslang::EvqVertexId: return spv::BuiltInVertexId; + case glslang::EvqInstanceId: return spv::BuiltInInstanceId; + case glslang::EvqFragCoord: return spv::BuiltInFragCoord; + case glslang::EvqPointCoord: return spv::BuiltInPointCoord; + case glslang::EvqFace: return spv::BuiltInFrontFacing; + case glslang::EvqFragColor: return spv::BuiltInFragColor; + case glslang::EvqFragDepth: return spv::BuiltInFragDepth; + default: return spv::BuiltInCount; + if (name == "gl_ClipDistance") + return spv::BuiltInClipDistance; + else if (name == "gl_PrimitiveID" || name == "gl_PrimitiveIDIn") + return spv::BuiltInPrimitiveId; + else if (name == "gl_InvocationID") + return spv::BuiltInInvocationId; + else if (name == "gl_Layer") + return spv::BuiltInLayer; + else if (name == "gl_ViewportIndex") + return spv::BuiltInViewportIndex; + else if (name == "gl_TessLevelOuter") + return spv::BuiltInTessLevelOuter; + else if (name == "gl_TessLevelInner") + return spv::BuiltInTessLevelInner; + else if (name == "gl_TessCoord") + return spv::BuiltInTessCoord; + else if (name == "gl_PatchVerticesIn") + return spv::BuiltInPatchVertices; + else if (name == "gl_SampleID") + return spv::BuiltInSampleId; + else if (name == "gl_SamplePosition") + return spv::BuiltInSamplePosition; + else if (name == "gl_SampleMask" || name == "gl_SampleMaskIn") + return spv::BuiltInSampleMask; + + // Compute shader: + else if (name == "gl_NumWorkGroups") + return spv::BuiltInNumWorkgroups; + else if (name == "gl_WorkGroupSize") + return spv::BuiltInWorkgroupSize; + else if (name == "gl_WorkGroupID") + return spv::BuiltInWorkgroupId; + else if (name == "gl_LocalInvocationID") + return spv::BuiltInLocalInvocationId; + else if (name == "gl_GlobalInvocationID") + return spv::BuiltInGlobalInvocationId; + else if (name == "gl_LocalInvocationIndexID") + return spv::BuiltInLocalInvocationIndex; + } +} + +// +// Implement the TGlslangToSpvTraverser class. +// + +TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* glslangIntermediate) + : TIntermTraverser(true, false, true), shaderEntry(0), sequenceDepth(0), + builder(GlslangMagic), + inMain(false), mainTerminated(false), linkageOnly(false), + glslangIntermediate(glslangIntermediate) +{ + spv::ExecutionModel executionModel = TranslateExecutionModel(glslangIntermediate->getStage()); + + builder.clearAccessChain(); + builder.setSource(spv::LangGLSL, glslangIntermediate->getVersion()); + stdBuiltins = builder.import("GLSL.std.450"); + builder.setMemoryModel(spv::AddressingLogical, spv::MemoryGLSL450); + shaderEntry = builder.makeMain(); + builder.addEntryPoint(executionModel, shaderEntry); + + // Add the source extensions + const std::set& sourceExtensions = glslangIntermediate->getRequestedExtensions(); + for (std::set::const_iterator it = sourceExtensions.begin(); it != sourceExtensions.end(); ++it) + builder.addSourceExtension(it->c_str()); + + // Add the top-level modes for this shader. + + if (glslangIntermediate->getXfbMode()) + builder.addExecutionMode(shaderEntry, spv::ExecutionXfb); + + spv::ExecutionMode mode; + switch (glslangIntermediate->getStage()) { + case EShLangVertex: + break; + + case EShLangTessControl: + builder.addExecutionMode(shaderEntry, spv::ExecutionOutputVertices, glslangIntermediate->getVertices()); + break; + + case EShLangTessEvaluation: + switch (glslangIntermediate->getInputPrimitive()) { + case glslang::ElgTriangles: mode = spv::ExecutionInputTriangles; break; + case glslang::ElgQuads: mode = spv::ExecutionInputQuads; break; + case glslang::ElgIsolines: mode = spv::ExecutionInputIsolines; break; + default: mode = spv::ExecutionModeCount; break; + } + if (mode != spv::ExecutionModeCount) + builder.addExecutionMode(shaderEntry, mode); + + // TODO + //builder.addExecutionMode(spv::VertexSpacingMdName, glslangIntermediate->getVertexSpacing()); + //builder.addExecutionMode(spv::VertexOrderMdName, glslangIntermediate->getVertexOrder()); + //builder.addExecutionMode(spv::PointModeMdName, glslangIntermediate->getPointMode()); + break; + + case EShLangGeometry: + switch (glslangIntermediate->getInputPrimitive()) { + case glslang::ElgPoints: mode = spv::ExecutionInputPoints; break; + case glslang::ElgLines: mode = spv::ExecutionInputLines; break; + case glslang::ElgLinesAdjacency: mode = spv::ExecutionInputLinesAdjacency; break; + case glslang::ElgTriangles: mode = spv::ExecutionInputTriangles; break; + case glslang::ElgTrianglesAdjacency: mode = spv::ExecutionInputTrianglesAdjacency; break; + default: mode = spv::ExecutionModeCount; break; + } + if (mode != spv::ExecutionModeCount) + builder.addExecutionMode(shaderEntry, mode); + builder.addExecutionMode(shaderEntry, spv::ExecutionInvocations, glslangIntermediate->getInvocations()); + + switch (glslangIntermediate->getOutputPrimitive()) { + case glslang::ElgPoints: mode = spv::ExecutionOutputPoints; break; + case glslang::ElgLineStrip: mode = spv::ExecutionOutputLineStrip; break; + case glslang::ElgTriangleStrip: mode = spv::ExecutionOutputTriangleStrip; break; + default: mode = spv::ExecutionModeCount; break; + } + if (mode != spv::ExecutionModeCount) + builder.addExecutionMode(shaderEntry, mode); + builder.addExecutionMode(shaderEntry, spv::ExecutionOutputVertices, glslangIntermediate->getVertices()); + break; + + case EShLangFragment: + if (glslangIntermediate->getPixelCenterInteger()) + builder.addExecutionMode(shaderEntry, spv::ExecutionPixelCenterInteger); + if (glslangIntermediate->getOriginUpperLeft()) + builder.addExecutionMode(shaderEntry, spv::ExecutionOriginUpperLeft); + break; + + case EShLangCompute: + break; + + default: + break; + } + +} + +TGlslangToSpvTraverser::~TGlslangToSpvTraverser() +{ + if (! mainTerminated) { + spv::Block* lastMainBlock = shaderEntry->getLastBlock(); + builder.setBuildPoint(lastMainBlock); + builder.leaveFunction(true); + } +} + +// +// Implement the traversal functions. +// +// Return true from interior nodes to have the external traversal +// continue on to children. Return false if children were +// already processed. +// + +// +// Symbols can turn into +// - uniform/input reads +// - output writes +// - complex lvalue base setups: foo.bar[3].... , where we see foo and start up an access chain +// - something simple that degenerates into the last bullet +// +void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol) +{ + // getSymbolId() will set up all the IO decorations on the first call. + // Formal function parameters were mapped during makeFunctions(). + spv::Id id = getSymbolId(symbol); + + if (! linkageOnly) { + // Prepare to generate code for the access + + // L-value chains will be computed left to right. We're on the symbol now, + // which is the left-most part of the access chain, so now is "clear" time, + // followed by setting the base. + builder.clearAccessChain(); + + // For now, we consider all user variables as being in memory, so they are pointers, + // except for "const in" arguments to a function, which are an intermediate object. + // See comments in handleUserFunctionCall(). + glslang::TStorageQualifier qualifier = symbol->getQualifier().storage; + if (qualifier == glslang::EvqConstReadOnly && constReadOnlyParameters.find(symbol->getId()) != constReadOnlyParameters.end()) + builder.setAccessChainRValue(id); + else + builder.setAccessChainLValue(id); + } +} + +bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::TIntermBinary* node) +{ + // First, handle special cases + switch (node->getOp()) { + case glslang::EOpAssign: + case glslang::EOpAddAssign: + case glslang::EOpSubAssign: + case glslang::EOpMulAssign: + case glslang::EOpVectorTimesMatrixAssign: + case glslang::EOpVectorTimesScalarAssign: + case glslang::EOpMatrixTimesScalarAssign: + case glslang::EOpMatrixTimesMatrixAssign: + case glslang::EOpDivAssign: + case glslang::EOpModAssign: + case glslang::EOpAndAssign: + case glslang::EOpInclusiveOrAssign: + case glslang::EOpExclusiveOrAssign: + case glslang::EOpLeftShiftAssign: + case glslang::EOpRightShiftAssign: + // A bin-op assign "a += b" means the same thing as "a = a + b" + // where a is evaluated before b. For a simple assignment, GLSL + // says to evaluate the left before the right. So, always, left + // node then right node. + { + // get the left l-value, save it away + builder.clearAccessChain(); + node->getLeft()->traverse(this); + spv::Builder::AccessChain lValue = builder.getAccessChain(); + + // evaluate the right + builder.clearAccessChain(); + node->getRight()->traverse(this); + spv::Id rValue = builder.accessChainLoad(TranslatePrecisionDecoration(node->getRight()->getType())); + + if (node->getOp() != glslang::EOpAssign) { + // the left is also an r-value + builder.setAccessChain(lValue); + spv::Id leftRValue = builder.accessChainLoad(TranslatePrecisionDecoration(node->getLeft()->getType())); + + // do the operation + rValue = createBinaryOperation(node->getOp(), TranslatePrecisionDecoration(node->getType()), + convertGlslangToSpvType(node->getType()), leftRValue, rValue, + node->getType().getBasicType()); + + // these all need their counterparts in createBinaryOperation() + if (rValue == 0) + spv::MissingFunctionality("createBinaryOperation"); + } + + // store the result + builder.setAccessChain(lValue); + builder.accessChainStore(rValue); + + // assignments are expressions having an rValue after they are evaluated... + builder.clearAccessChain(); + builder.setAccessChainRValue(rValue); + } + return false; + case glslang::EOpIndexDirect: + case glslang::EOpIndexDirectStruct: + { + // Get the left part of the access chain. + node->getLeft()->traverse(this); + + // Add the next element in the chain + + int index = 0; + if (node->getRight()->getAsConstantUnion() == 0) + spv::MissingFunctionality("direct index without a constant node"); + else + index = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + + if (node->getLeft()->getBasicType() == glslang::EbtBlock && node->getOp() == glslang::EOpIndexDirectStruct) { + // This may be, e.g., an anonymous block-member selection, which generally need + // index remapping due to hidden members in anonymous blocks. + std::vector& remapper = memberRemapper[node->getLeft()->getType().getStruct()]; + if (remapper.size() == 0) + spv::MissingFunctionality("block without member remapping"); + else + index = remapper[index]; + } + + if (! node->getLeft()->getType().isArray() && + node->getLeft()->getType().isVector() && + node->getOp() == glslang::EOpIndexDirect) { + // This is essentially a hard-coded vector swizzle of size 1, + // so short circuit the access-chain stuff with a swizzle. + std::vector swizzle; + swizzle.push_back(node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst()); + builder.accessChainPushSwizzle(swizzle, node->getLeft()->getVectorSize(), convertGlslangToSpvType(node->getType())); + } else { + // normal case for indexing array or structure or block + builder.accessChainPush(builder.makeIntConstant(index), convertGlslangToSpvType(node->getType())); + } + } + return false; + case glslang::EOpIndexIndirect: + { + // Structure or array or vector indirection. + // Will use native SPIR-V access-chain for struct and array indirection; + // matrices are arrays of vectors, so will also work for a matrix. + // Will use the access chain's 'component' for variable index into a vector. + + // This adapter is building access chains left to right. + // Set up the access chain to the left. + node->getLeft()->traverse(this); + + // save it so that computing the right side doesn't trash it + spv::Builder::AccessChain partial = builder.getAccessChain(); + + // compute the next index in the chain + builder.clearAccessChain(); + node->getRight()->traverse(this); + spv::Id index = builder.accessChainLoad(TranslatePrecisionDecoration(node->getRight()->getType())); + + // restore the saved access chain + builder.setAccessChain(partial); + + if (! node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector()) + builder.accessChainPushComponent(index); + else + builder.accessChainPush(index, convertGlslangToSpvType(node->getType())); + } + return false; + case glslang::EOpVectorSwizzle: + { + node->getLeft()->traverse(this); + glslang::TIntermSequence& swizzleSequence = node->getRight()->getAsAggregate()->getSequence(); + std::vector swizzle; + for (int i = 0; i < (int)swizzleSequence.size(); ++i) + swizzle.push_back(swizzleSequence[i]->getAsConstantUnion()->getConstArray()[0].getIConst()); + builder.accessChainPushSwizzle(swizzle, node->getLeft()->getVectorSize(), convertGlslangToSpvType(node->getType())); + } + return false; + default: + break; + } + + // Assume generic binary op... + + // Get the operands + builder.clearAccessChain(); + node->getLeft()->traverse(this); + spv::Id left = builder.accessChainLoad(TranslatePrecisionDecoration(node->getLeft()->getType())); + + builder.clearAccessChain(); + node->getRight()->traverse(this); + spv::Id right = builder.accessChainLoad(TranslatePrecisionDecoration(node->getRight()->getType())); + + spv::Id result; + spv::Decoration precision = TranslatePrecisionDecoration(node->getType()); + + result = createBinaryOperation(node->getOp(), precision, + convertGlslangToSpvType(node->getType()), left, right, + node->getLeft()->getType().getBasicType()); + + if (! result) { + spv::MissingFunctionality("glslang binary operation"); + } else { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; + } + + return true; +} + +bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) +{ + builder.clearAccessChain(); + node->getOperand()->traverse(this); + spv::Id operand = builder.accessChainLoad(TranslatePrecisionDecoration(node->getOperand()->getType())); + + spv::Decoration precision = TranslatePrecisionDecoration(node->getType()); + + // it could be a conversion + spv::Id result = createConversion(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operand); + + // if not, then possibly an operation + if (! result) + result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operand, node->getBasicType() == glslang::EbtFloat || node->getBasicType() == glslang::EbtDouble); + + if (result) { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; // done with this node + } + + // it must be a special case, check... + switch (node->getOp()) { + case glslang::EOpPostIncrement: + case glslang::EOpPostDecrement: + case glslang::EOpPreIncrement: + case glslang::EOpPreDecrement: + { + // we need the integer value "1" or the floating point "1.0" to add/subtract + spv::Id one = node->getBasicType() == glslang::EbtFloat ? + builder.makeFloatConstant(1.0F) : + builder.makeIntConstant(1); + glslang::TOperator op; + if (node->getOp() == glslang::EOpPreIncrement || + node->getOp() == glslang::EOpPostIncrement) + op = glslang::EOpAdd; + else + op = glslang::EOpSub; + + spv::Id result = createBinaryOperation(op, TranslatePrecisionDecoration(node->getType()), + convertGlslangToSpvType(node->getType()), operand, one, + node->getType().getBasicType()); + if (result == 0) + spv::MissingFunctionality("createBinaryOperation for unary"); + + // The result of operation is always stored, but conditionally the + // consumed result. The consumed result is always an r-value. + builder.accessChainStore(result); + builder.clearAccessChain(); + if (node->getOp() == glslang::EOpPreIncrement || + node->getOp() == glslang::EOpPreDecrement) + builder.setAccessChainRValue(result); + else + builder.setAccessChainRValue(operand); + } + + return false; + default: + spv::MissingFunctionality("glslang unary"); + break; + } + + return true; +} + +bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TIntermAggregate* node) +{ + spv::Id result; + glslang::TOperator binOp = glslang::EOpNull; + bool reduceComparison = true; + bool isMatrix = false; + bool noReturnValue = false; + + assert(node->getOp()); + + spv::Decoration precision = TranslatePrecisionDecoration(node->getType()); + + switch (node->getOp()) { + case glslang::EOpSequence: + { + if (preVisit) + ++sequenceDepth; + else + --sequenceDepth; + + if (sequenceDepth == 1) { + // If this is the parent node of all the functions, we want to see them + // early, so all call points have actual SPIR-V functions to reference. + // In all cases, still let the traverser visit the children for us. + makeFunctions(node->getAsAggregate()->getSequence()); + + // Also, we want all globals initializers to go into the entry of main(), before + // anything else gets there, so visit out of order, doing them all now. + makeGlobalInitializers(node->getAsAggregate()->getSequence()); + + // Initializers are done, don't want to visit again, but functions link objects need to be processed, + // so do them manually. + visitFunctions(node->getAsAggregate()->getSequence()); + + return false; + } + + return true; + } + case glslang::EOpLinkerObjects: + { + if (visit == glslang::EvPreVisit) + linkageOnly = true; + else + linkageOnly = false; + + return true; + } + case glslang::EOpComma: + { + // processing from left to right naturally leaves the right-most + // lying around in the access chain + glslang::TIntermSequence& glslangOperands = node->getSequence(); + for (int i = 0; i < (int)glslangOperands.size(); ++i) + glslangOperands[i]->traverse(this); + + return false; + } + case glslang::EOpFunction: + if (visit == glslang::EvPreVisit) { + if (isShaderEntrypoint(node)) { + inMain = true; + builder.setBuildPoint(shaderEntry->getLastBlock()); + } else { + handleFunctionEntry(node); + } + } else { + if (inMain) + mainTerminated = true; + builder.leaveFunction(inMain); + inMain = false; + } + + return true; + case glslang::EOpParameters: + // Parameters will have been consumed by EOpFunction processing, but not + // the body, so we still visited the function node's children, making this + // child redundant. + return false; + case glslang::EOpFunctionCall: + { + if (node->isUserDefined()) + result = handleUserFunctionCall(node); + else + result = handleBuiltInFunctionCall(node); + + if (! result) { + spv::MissingFunctionality("glslang function call"); + glslang::TConstUnionArray emptyConsts; + int nextConst = 0; + result = createSpvConstant(node->getType(), emptyConsts, nextConst); + } + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; + } + case glslang::EOpConstructMat2x2: + case glslang::EOpConstructMat2x3: + case glslang::EOpConstructMat2x4: + case glslang::EOpConstructMat3x2: + case glslang::EOpConstructMat3x3: + case glslang::EOpConstructMat3x4: + case glslang::EOpConstructMat4x2: + case glslang::EOpConstructMat4x3: + case glslang::EOpConstructMat4x4: + case glslang::EOpConstructDMat2x2: + case glslang::EOpConstructDMat2x3: + case glslang::EOpConstructDMat2x4: + case glslang::EOpConstructDMat3x2: + case glslang::EOpConstructDMat3x3: + case glslang::EOpConstructDMat3x4: + case glslang::EOpConstructDMat4x2: + case glslang::EOpConstructDMat4x3: + case glslang::EOpConstructDMat4x4: + isMatrix = true; + // fall through + case glslang::EOpConstructFloat: + case glslang::EOpConstructVec2: + case glslang::EOpConstructVec3: + case glslang::EOpConstructVec4: + case glslang::EOpConstructDouble: + case glslang::EOpConstructDVec2: + case glslang::EOpConstructDVec3: + case glslang::EOpConstructDVec4: + case glslang::EOpConstructBool: + case glslang::EOpConstructBVec2: + case glslang::EOpConstructBVec3: + case glslang::EOpConstructBVec4: + case glslang::EOpConstructInt: + case glslang::EOpConstructIVec2: + case glslang::EOpConstructIVec3: + case glslang::EOpConstructIVec4: + case glslang::EOpConstructUint: + case glslang::EOpConstructUVec2: + case glslang::EOpConstructUVec3: + case glslang::EOpConstructUVec4: + case glslang::EOpConstructStruct: + { + std::vector arguments; + translateArguments(node->getSequence(), arguments); + spv::Id resultTypeId = convertGlslangToSpvType(node->getType()); + spv::Id constructed; + if (node->getOp() == glslang::EOpConstructStruct || node->getType().isArray()) { + std::vector constituents; + for (int c = 0; c < (int)arguments.size(); ++c) + constituents.push_back(arguments[c]); + constructed = builder.createCompositeConstruct(resultTypeId, constituents); + } else { + if (isMatrix) + constructed = builder.createMatrixConstructor(precision, arguments, resultTypeId); + else + constructed = builder.createConstructor(precision, arguments, resultTypeId); + } + + builder.clearAccessChain(); + builder.setAccessChainRValue(constructed); + + return false; + } + + // These six are component-wise compares with component-wise results. + // Forward on to createBinaryOperation(), requesting a vector result. + case glslang::EOpLessThan: + case glslang::EOpGreaterThan: + case glslang::EOpLessThanEqual: + case glslang::EOpGreaterThanEqual: + case glslang::EOpVectorEqual: + case glslang::EOpVectorNotEqual: + { + // Map the operation to a binary + binOp = node->getOp(); + reduceComparison = false; + switch (node->getOp()) { + case glslang::EOpVectorEqual: binOp = glslang::EOpVectorEqual; break; + case glslang::EOpVectorNotEqual: binOp = glslang::EOpVectorNotEqual; break; + default: binOp = node->getOp(); break; + } + + break; + } + case glslang::EOpMul: + // compontent-wise matrix multiply + binOp = glslang::EOpMul; + break; + case glslang::EOpOuterProduct: + // two vectors multiplied to make a matrix + binOp = glslang::EOpOuterProduct; + break; + case glslang::EOpDot: + { + // for scalar dot product, use multiply + glslang::TIntermSequence& glslangOperands = node->getSequence(); + if (! glslangOperands[0]->getAsTyped()->isVector()) + binOp = glslang::EOpMul; + break; + } + case glslang::EOpMod: + // when an aggregate, this is the floating-point mod built-in function, + // which can be emitted by the one in createBinaryOperation() + binOp = glslang::EOpMod; + break; + case glslang::EOpArrayLength: + { + glslang::TIntermTyped* typedNode = node->getSequence()[0]->getAsTyped(); + assert(typedNode); + spv::Id length = builder.makeIntConstant(typedNode->getType().getArraySize()); + + builder.clearAccessChain(); + builder.setAccessChainRValue(length); + + return false; + } + case glslang::EOpEmitVertex: + case glslang::EOpEndPrimitive: + case glslang::EOpBarrier: + case glslang::EOpMemoryBarrier: + case glslang::EOpMemoryBarrierAtomicCounter: + case glslang::EOpMemoryBarrierBuffer: + case glslang::EOpMemoryBarrierImage: + case glslang::EOpMemoryBarrierShared: + case glslang::EOpGroupMemoryBarrier: + noReturnValue = true; + // These all have 0 operands and will naturally finish up in the createIntrinsic code below for 0 operands + break; + + case glslang::EOpEmitStreamVertex: + case glslang::EOpEndStreamPrimitive: + noReturnValue = true; + // These all have 1 operand and will naturally finish up in the createIntrinsic code below for 1 operand + break; + + default: + break; + } + + // + // See if it maps to a regular operation. + // + + if (binOp != glslang::EOpNull) { + glslang::TIntermTyped* left = node->getSequence()[0]->getAsTyped(); + glslang::TIntermTyped* right = node->getSequence()[1]->getAsTyped(); + assert(left && right); + + builder.clearAccessChain(); + left->traverse(this); + spv::Id leftId = builder.accessChainLoad(TranslatePrecisionDecoration(left->getType())); + + builder.clearAccessChain(); + right->traverse(this); + spv::Id rightId = builder.accessChainLoad(TranslatePrecisionDecoration(right->getType())); + + result = createBinaryOperation(binOp, precision, + convertGlslangToSpvType(node->getType()), leftId, rightId, + left->getType().getBasicType(), reduceComparison); + + // code above should only make binOp that exists in createBinaryOperation + if (result == 0) + spv::MissingFunctionality("createBinaryOperation for aggregate"); + + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; + } + + glslang::TIntermSequence& glslangOperands = node->getSequence(); + std::vector operands; + for (int arg = 0; arg < (int)glslangOperands.size(); ++arg) { + builder.clearAccessChain(); + glslangOperands[arg]->traverse(this); + + // special case l-value operands; there are just a few + bool lvalue = false; + switch (node->getOp()) { + //case glslang::EOpFrexp: + case glslang::EOpModf: + if (arg == 1) + lvalue = true; + break; + //case glslang::EOpUAddCarry: + //case glslang::EOpUSubBorrow: + //case glslang::EOpUMulExtended: + default: + break; + } + if (lvalue) + operands.push_back(builder.accessChainGetLValue()); + else + operands.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangOperands[arg]->getAsTyped()->getType()))); + } + switch (glslangOperands.size()) { + case 0: + result = createNoArgOperation(node->getOp()); + break; + case 1: + result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands.front(), node->getType().getBasicType() == glslang::EbtFloat || node->getType().getBasicType() == glslang::EbtDouble); + break; + default: + { + const glslang::TType& type = glslangOperands.front()->getAsTyped()->getType(); + result = createMiscOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands, type.getBasicType() == glslang::EbtUint); + break; + } + } + + if (noReturnValue) + return false; + + if (! result) { + spv::MissingFunctionality("glslang aggregate"); + return true; + } else { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + return false; + } +} + +bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang::TIntermSelection* node) +{ + // This path handles both if-then-else and ?: + // The if-then-else has a node type of void, while + // ?: has a non-void node type + spv::Id result = 0; + if (node->getBasicType() != glslang::EbtVoid) { + // don't handle this as just on-the-fly temporaries, because there will be two names + // and better to leave SSA to later passes + result = builder.createVariable(spv::StorageFunction, convertGlslangToSpvType(node->getType())); + } + + // emit the condition before doing anything with selection + node->getCondition()->traverse(this); + + // make an "if" based on the value created by the condition + spv::Builder::If ifBuilder(builder.accessChainLoad(spv::DecCount), builder); + + if (node->getTrueBlock()) { + // emit the "then" statement + node->getTrueBlock()->traverse(this); + if (result) + builder.createStore(builder.accessChainLoad(TranslatePrecisionDecoration(node->getTrueBlock()->getAsTyped()->getType())), result); + } + + if (node->getFalseBlock()) { + ifBuilder.makeBeginElse(); + // emit the "else" statement + node->getFalseBlock()->traverse(this); + if (result) + builder.createStore(builder.accessChainLoad(TranslatePrecisionDecoration(node->getFalseBlock()->getAsTyped()->getType())), result); + } + + ifBuilder.makeEndIf(); + + if (result) { + // GLSL only has r-values as the result of a :?, but + // if we have an l-value, that can be more efficient if it will + // become the base of a complex r-value expression, because the + // next layer copies r-values into memory to use the access-chain mechanism + builder.clearAccessChain(); + builder.setAccessChainLValue(result); + } + + return false; +} + +bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::TIntermSwitch* node) +{ + // emit and get the condition before doing anything with switch + node->getCondition()->traverse(this); + spv::Id selector = builder.accessChainLoad(TranslatePrecisionDecoration(node->getCondition()->getAsTyped()->getType())); + + // browse the children to sort out code segments + int defaultSegment = -1; + std::vector codeSegments; + glslang::TIntermSequence& sequence = node->getBody()->getSequence(); + std::vector caseValues; + std::vector valueIndexToSegment(sequence.size()); // note: probably not all are used, it is an overestimate + for (glslang::TIntermSequence::iterator c = sequence.begin(); c != sequence.end(); ++c) { + TIntermNode* child = *c; + if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpDefault) + defaultSegment = codeSegments.size(); + else if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpCase) { + valueIndexToSegment[caseValues.size()] = codeSegments.size(); + caseValues.push_back(child->getAsBranchNode()->getExpression()->getAsConstantUnion()->getConstArray()[0].getIConst()); + } else + codeSegments.push_back(child); + } + + // handle the case where the last code segment is missing, due to no code + // statements between the last case and the end of the switch statement + if ((int)codeSegments.size() == valueIndexToSegment[caseValues.size() - 1]) + codeSegments.push_back(0); + + // make the switch statement + std::vector segmentBlocks; // returned, as the blocks allocated in the call + builder.makeSwitch(selector, codeSegments.size(), caseValues, valueIndexToSegment, defaultSegment, segmentBlocks); + + // emit all the code in the segments + breakForLoop.push(false); + for (unsigned int s = 0; s < codeSegments.size(); ++s) { + builder.nextSwitchSegment(segmentBlocks, s); + if (codeSegments[s]) + codeSegments[s]->traverse(this); + else + builder.addSwitchBreak(); + } + breakForLoop.pop(); + + builder.endSwitch(segmentBlocks); + + return false; +} + +void TGlslangToSpvTraverser::visitConstantUnion(glslang::TIntermConstantUnion* node) +{ + int nextConst = 0; + spv::Id constant = createSpvConstant(node->getType(), node->getConstArray(), nextConst); + + builder.clearAccessChain(); + builder.setAccessChainRValue(constant); +} + +bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIntermLoop* node) +{ + // body emission needs to know what the for-loop terminal is when it sees a "continue" + loopTerminal.push(node->getTerminal()); + + builder.makeNewLoop(); + + bool bodyOut = false; + if (! node->testFirst()) { + if (node->getBody()) { + breakForLoop.push(true); + node->getBody()->traverse(this); + breakForLoop.pop(); + } + bodyOut = true; + } + + if (node->getTest()) { + node->getTest()->traverse(this); + // the AST only contained the test computation, not the branch, we have to add it + spv::Id condition = builder.accessChainLoad(TranslatePrecisionDecoration(node->getTest()->getType())); + builder.createLoopHeaderBranch(condition); + } + + if (! bodyOut && node->getBody()) { + breakForLoop.push(true); + node->getBody()->traverse(this); + breakForLoop.pop(); + } + + if (loopTerminal.top()) + loopTerminal.top()->traverse(this); + + builder.closeLoop(); + + loopTerminal.pop(); + + return false; +} + +bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::TIntermBranch* node) +{ + if (node->getExpression()) + node->getExpression()->traverse(this); + + switch (node->getFlowOp()) { + case glslang::EOpKill: + builder.makeDiscard(); + break; + case glslang::EOpBreak: + if (breakForLoop.top()) + builder.createLoopExit(); + else + builder.addSwitchBreak(); + break; + case glslang::EOpContinue: + if (loopTerminal.top()) + loopTerminal.top()->traverse(this); + builder.createLoopBackEdge(); + break; + case glslang::EOpReturn: + if (inMain) + builder.makeMainReturn(); + else if (node->getExpression()) + builder.makeReturn(false, builder.accessChainLoad(TranslatePrecisionDecoration(node->getExpression()->getType()))); + else + builder.makeReturn(); + + builder.clearAccessChain(); + break; + + default: + spv::MissingFunctionality("branch type"); + break; + } + + return false; +} + +spv::Id TGlslangToSpvTraverser::createSpvVariable(const glslang::TIntermSymbol* node) +{ + // First, steer off constants, which are not SPIR-V variables, but + // can still have a mapping to a SPIR-V Id. + if (node->getQualifier().storage == glslang::EvqConst) { + int nextConst = 0; + return createSpvConstant(node->getType(), node->getConstArray(), nextConst); + } + + // Now, handle actual variables + spv::StorageClass storageClass = TranslateStorageClass(node->getType()); + spv::Id spvType = convertGlslangToSpvType(node->getType()); + + const char* name = node->getName().c_str(); + if (glslang::IsAnonymous(name)) + name = ""; + + if (storageClass == spv::StorageCount) + return builder.createVariable(spv::StorageFunction, spvType, name); + else + return builder.createVariable(storageClass, spvType, name); +} + +// Return type Id of the sampled type. +spv::Id TGlslangToSpvTraverser::getSampledType(const glslang::TSampler& sampler) +{ + switch (sampler.type) { + case glslang::EbtFloat: return builder.makeFloatType(32); + case glslang::EbtInt: return builder.makeIntType(32); + case glslang::EbtUint: return builder.makeUintType(32); + default: + spv::MissingFunctionality("sampled type"); + return builder.makeFloatType(32); + } +} + +// Do full recursive conversion of an arbitrary glslang type to a SPIR-V Id. +spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type) +{ + spv::Id spvType; + + switch (type.getBasicType()) { + case glslang::EbtVoid: + spvType = builder.makeVoidType(); + if (type.isArray()) + spv::MissingFunctionality("array of void"); + break; + case glslang::EbtFloat: + spvType = builder.makeFloatType(32); + break; + case glslang::EbtDouble: + spvType = builder.makeFloatType(64); + break; + case glslang::EbtBool: + spvType = builder.makeBoolType(); + break; + case glslang::EbtInt: + spvType = builder.makeIntType(32); + break; + case glslang::EbtUint: + spvType = builder.makeUintType(32); + break; + case glslang::EbtSampler: + { + const glslang::TSampler& sampler = type.getSampler(); + spvType = builder.makeSampler(getSampledType(sampler), TranslateDimensionality(sampler), + sampler.image ? spv::Builder::samplerContentImage : spv::Builder::samplerContentTextureFilter, + sampler.arrayed, sampler.shadow, sampler.ms); + } + break; + case glslang::EbtStruct: + case glslang::EbtBlock: + { + // If we've seen this struct type, return it + const glslang::TTypeList* glslangStruct = type.getStruct(); + std::vector structFields; + spvType = structMap[glslangStruct]; + if (spvType) + break; + + // else, we haven't seen it... + + // Create a vector of struct types for SPIR-V to consume + int memberDelta = 0; // how much the member's index changes from glslang to SPIR-V, normally 0, except sometimes for blocks + if (type.getBasicType() == glslang::EbtBlock) + memberRemapper[glslangStruct].resize(glslangStruct->size()); + for (int i = 0; i < (int)glslangStruct->size(); i++) { + glslang::TType& glslangType = *(*glslangStruct)[i].type; + if (glslangType.hiddenMember()) { + ++memberDelta; + if (type.getBasicType() == glslang::EbtBlock) + memberRemapper[glslangStruct][i] = -1; + } else { + if (type.getBasicType() == glslang::EbtBlock) + memberRemapper[glslangStruct][i] = i - memberDelta; + structFields.push_back(convertGlslangToSpvType(glslangType)); + } + } + + // Make the SPIR-V type + spvType = builder.makeStructType(structFields, type.getTypeName().c_str()); + structMap[glslangStruct] = spvType; + + // Name and decorate the non-hidden members + for (int i = 0; i < (int)glslangStruct->size(); i++) { + glslang::TType& glslangType = *(*glslangStruct)[i].type; + int member = i; + if (type.getBasicType() == glslang::EbtBlock) + member = memberRemapper[glslangStruct][i]; + // using -1 above to indicate a hidden member + if (member >= 0) { + builder.addMemberName(spvType, member, glslangType.getFieldName().c_str()); + addMemberDecoration(spvType, member, TranslateLayoutDecoration(glslangType)); + addMemberDecoration(spvType, member, TranslatePrecisionDecoration(glslangType)); + addMemberDecoration(spvType, member, TranslateInterpolationDecoration(glslangType)); + addMemberDecoration(spvType, member, TranslateInvariantDecoration(glslangType)); + if (glslangType.getQualifier().hasLocation()) + builder.addMemberDecoration(spvType, member, spv::DecLocation, glslangType.getQualifier().layoutLocation); + if (glslangType.getQualifier().hasComponent()) + builder.addMemberDecoration(spvType, member, spv::DecComponent, glslangType.getQualifier().layoutComponent); + if (glslangType.getQualifier().hasXfbOffset()) + builder.addMemberDecoration(spvType, member, spv::DecOffset, glslangType.getQualifier().layoutXfbOffset); + } + } + + // Decorate the structure + addDecoration(spvType, TranslateLayoutDecoration(type)); + addDecoration(spvType, TranslateBlockDecoration(type)); + if (type.getQualifier().hasStream()) + builder.addDecoration(spvType, spv::DecStream, type.getQualifier().layoutStream); + if (glslangIntermediate->getXfbMode()) { + if (type.getQualifier().hasXfbStride()) + builder.addDecoration(spvType, spv::DecStride, type.getQualifier().layoutXfbStride); + if (type.getQualifier().hasXfbBuffer()) + builder.addDecoration(spvType, spv::DecXfbBuffer, type.getQualifier().layoutXfbBuffer); + } + } + break; + default: + spv::MissingFunctionality("basic type"); + break; + } + + if (type.isMatrix()) + spvType = builder.makeMatrixType(spvType, type.getMatrixCols(), type.getMatrixRows()); + else { + // If this variable has a vector element count greater than 1, create a SPIR-V vector + if (type.getVectorSize() > 1) + spvType = builder.makeVectorType(spvType, type.getVectorSize()); + } + + if (type.isArray()) { + unsigned arraySize; + if (! type.isExplicitlySizedArray()) { + spv::MissingFunctionality("Unsized array"); + arraySize = 8; + } else + arraySize = type.getArraySize(); + spvType = builder.makeArrayType(spvType, arraySize); + } + + return spvType; +} + +bool TGlslangToSpvTraverser::isShaderEntrypoint(const glslang::TIntermAggregate* node) +{ + return node->getName() == "main("; +} + +// Make all the functions, skeletally, without actually visiting their bodies. +void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslFunctions) +{ + for (int f = 0; f < (int)glslFunctions.size(); ++f) { + glslang::TIntermAggregate* glslFunction = glslFunctions[f]->getAsAggregate(); + if (! glslFunction || glslFunction->getOp() != glslang::EOpFunction || isShaderEntrypoint(glslFunction)) + continue; + + // We're on a user function. Set up the basic interface for the function now, + // so that it's available to call. + // Translating the body will happen later. + // + // Typically (except for a "const in" parameter), an address will be passed to the + // function. What it is an address of varies: + // + // - "in" parameters not marked as "const" can be written to without modifying the argument, + // so that write needs to be to a copy, hence the address of a copy works. + // + // - "const in" parameters can just be the r-value, as no writes need occur. + // + // - "out" and "inout" arguments can't be done as direct pointers, because GLSL has + // copy-in/copy-out semantics. They can be handled though with a pointer to a copy. + + std::vector paramTypes; + glslang::TIntermSequence& parameters = glslFunction->getSequence()[0]->getAsAggregate()->getSequence(); + + for (int p = 0; p < (int)parameters.size(); ++p) { + const glslang::TType& paramType = parameters[p]->getAsTyped()->getType(); + spv::Id typeId = convertGlslangToSpvType(paramType); + if (paramType.getQualifier().storage != glslang::EvqConstReadOnly) + typeId = builder.makePointer(spv::StorageFunction, typeId); + else + constReadOnlyParameters.insert(parameters[p]->getAsSymbolNode()->getId()); + paramTypes.push_back(typeId); + } + + spv::Block* functionBlock; + spv::Function *function = builder.makeFunctionEntry(convertGlslangToSpvType(glslFunction->getType()), glslFunction->getName().c_str(), + paramTypes, &functionBlock); + + // Track function to emit/call later + functionMap[glslFunction->getName().c_str()] = function; + + // Set the parameter id's + for (int p = 0; p < (int)parameters.size(); ++p) { + symbolValues[parameters[p]->getAsSymbolNode()->getId()] = function->getParamId(p); + // give a name too + builder.addName(function->getParamId(p), parameters[p]->getAsSymbolNode()->getName().c_str()); + } + } +} + +// Process all the initializers, while skipping the functions and link objects +void TGlslangToSpvTraverser::makeGlobalInitializers(const glslang::TIntermSequence& initializers) +{ + builder.setBuildPoint(shaderEntry->getLastBlock()); + for (int i = 0; i < (int)initializers.size(); ++i) { + glslang::TIntermAggregate* initializer = initializers[i]->getAsAggregate(); + if (initializer && initializer->getOp() != glslang::EOpFunction && initializer->getOp() != glslang::EOpLinkerObjects) { + + // We're on a top-level node that's not a function. Treat as an initializer, whose + // code goes into the beginning of main. + initializer->traverse(this); + } + } +} + +// Process all the functions, while skipping initializers. +void TGlslangToSpvTraverser::visitFunctions(const glslang::TIntermSequence& glslFunctions) +{ + for (int f = 0; f < (int)glslFunctions.size(); ++f) { + glslang::TIntermAggregate* node = glslFunctions[f]->getAsAggregate(); + if (node && (node->getOp() == glslang::EOpFunction || node->getOp() == glslang ::EOpLinkerObjects)) + node->traverse(this); + } +} + +void TGlslangToSpvTraverser::handleFunctionEntry(const glslang::TIntermAggregate* node) +{ + // SPIR-V functions should already be in the functionMap from the prepass + // that called makeFunctions(). + spv::Function* function = functionMap[node->getName().c_str()]; + spv::Block* functionBlock = function->getEntryBlock(); + builder.setBuildPoint(functionBlock); +} + +void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermSequence& glslangArguments, std::vector& arguments) +{ + for (int i = 0; i < (int)glslangArguments.size(); ++i) { + builder.clearAccessChain(); + glslangArguments[i]->traverse(this); + arguments.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangArguments[i]->getAsTyped()->getType()))); + } +} + +spv::Id TGlslangToSpvTraverser::handleBuiltInFunctionCall(const glslang::TIntermAggregate* node) +{ + std::vector arguments; + translateArguments(node->getSequence(), arguments); + + std::vector argTypes; + for (int a = 0; a < (int)arguments.size(); ++a) + argTypes.push_back(builder.getTypeId(arguments[a])); + + spv::Decoration precision = TranslatePrecisionDecoration(node->getType()); + + if (node->getName() == "ftransform(") { + spv::MissingFunctionality("ftransform()"); + //spv::Id vertex = builder.createVariable(spv::StorageShaderGlobal, spv::VectorType::get(spv::makeFloatType(), 4), + // "gl_Vertex_sim"); + //spv::Id matrix = builder.createVariable(spv::StorageShaderGlobal, spv::VectorType::get(spv::makeFloatType(), 4), + // "gl_ModelViewProjectionMatrix_sim"); + return 0; + } + + if (node->getName().substr(0, 7) == "texture" || node->getName().substr(0, 5) == "texel" || node->getName().substr(0, 6) == "shadow") { + const glslang::TSampler sampler = node->getSequence()[0]->getAsTyped()->getType().getSampler(); + spv::Builder::TextureParameters params = { }; + params.sampler = arguments[0]; + + // special case size query + if (node->getName().find("textureSize", 0) != std::string::npos) { + if (arguments.size() > 1) { + params.lod = arguments[1]; + return builder.createTextureQueryCall(spv::OpTextureQuerySizeLod, params); + } else + return builder.createTextureQueryCall(spv::OpTextureQuerySize, params); + } + + // special case the number of samples query + if (node->getName().find("textureSamples", 0) != std::string::npos) + return builder.createTextureQueryCall(spv::OpTextureQuerySamples, params); + + // special case the other queries + if (node->getName().find("Query", 0) != std::string::npos) { + if (node->getName().find("Levels", 0) != std::string::npos) + return builder.createTextureQueryCall(spv::OpTextureQueryLevels, params); + else if (node->getName().find("Lod", 0) != std::string::npos) { + params.coords = arguments[1]; + return builder.createTextureQueryCall(spv::OpTextureQueryLod, params); + } else + spv::MissingFunctionality("glslang texture query"); + } + + // This is no longer a query.... + + bool lod = node->getName().find("Lod", 0) != std::string::npos; + bool proj = node->getName().find("Proj", 0) != std::string::npos; + bool offsets = node->getName().find("Offsets", 0) != std::string::npos; + bool offset = ! offsets && node->getName().find("Offset", 0) != std::string::npos; + bool fetch = node->getName().find("Fetch", 0) != std::string::npos; + bool gather = node->getName().find("Gather", 0) != std::string::npos; + bool grad = node->getName().find("Grad", 0) != std::string::npos; + + if (fetch) + spv::MissingFunctionality("texel fetch"); + if (gather) + spv::MissingFunctionality("texture gather"); + + // check for bias argument + bool bias = false; + if (! lod && ! gather && ! grad && ! fetch) { + int nonBiasArgCount = 2; + if (offset) + ++nonBiasArgCount; + if (grad) + nonBiasArgCount += 2; + + if ((int)arguments.size() > nonBiasArgCount) + bias = true; + } + + bool cubeCompare = sampler.dim == glslang::EsdCube && sampler.arrayed && sampler.shadow; + + // set the rest of the arguments + params.coords = arguments[1]; + int extraArgs = 0; + if (cubeCompare) + params.Dref = arguments[2]; + if (lod) { + params.lod = arguments[2]; + ++extraArgs; + } + if (grad) { + params.gradX = arguments[2 + extraArgs]; + params.gradY = arguments[3 + extraArgs]; + extraArgs += 2; + } + //if (gather && compare) { + // params.compare = arguments[2 + extraArgs]; + // ++extraArgs; + //} + if (offset | offsets) { + params.offset = arguments[2 + extraArgs]; + ++extraArgs; + } + if (bias) { + params.bias = arguments[2 + extraArgs]; + ++extraArgs; + } + + return builder.createTextureCall(precision, convertGlslangToSpvType(node->getType()), proj, params); + } + + spv::MissingFunctionality("built-in function call"); + + return 0; +} + +spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAggregate* node) +{ + // Grab the function's pointer from the previously created function + spv::Function* function = functionMap[node->getName().c_str()]; + if (! function) + return 0; + + const glslang::TIntermSequence& glslangArgs = node->getSequence(); + const glslang::TQualifierList& qualifiers = node->getQualifierList(); + + // See comments in makeFunctions() for details about the semantics for parameter passing. + // + // These imply we need a four step process: + // 1. Evaluate the arguments + // 2. Allocate and make copies of in, out, and inout arguments + // 3. Make the call + // 4. Copy back the results + + // 1. Evaluate the arguments + std::vector lValues; + std::vector rValues; + for (int a = 0; a < (int)glslangArgs.size(); ++a) { + // build l-value + builder.clearAccessChain(); + glslangArgs[a]->traverse(this); + // keep outputs as l-values, evaluate input-only as r-values + if (qualifiers[a] != glslang::EvqConstReadOnly) { + // save l-value + lValues.push_back(builder.getAccessChain()); + } else { + // process r-value + rValues.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangArgs[a]->getAsTyped()->getType()))); + } + } + + // 2. Allocate space for anything needing a copy, and if it's "in" or "inout" + // copy the original into that space. + // + // Also, build up the list of actual arguments to pass in for the call + int lValueCount = 0; + int rValueCount = 0; + std::vector spvArgs; + for (int a = 0; a < (int)glslangArgs.size(); ++a) { + spv::Id arg; + if (qualifiers[a] != glslang::EvqConstReadOnly) { + // need space to hold the copy + const glslang::TType& paramType = glslangArgs[a]->getAsTyped()->getType(); + arg = builder.createVariable(spv::StorageFunction, convertGlslangToSpvType(paramType), "param"); + if (qualifiers[a] == glslang::EvqIn || qualifiers[a] == glslang::EvqInOut) { + // need to copy the input into output space + builder.setAccessChain(lValues[lValueCount]); + spv::Id copy = builder.accessChainLoad(spv::DecCount); // TODO: get precision + builder.createStore(copy, arg); + } + ++lValueCount; + } else { + arg = rValues[rValueCount]; + ++rValueCount; + } + spvArgs.push_back(arg); + } + + // 3. Make the call. + spv::Id result = builder.createFunctionCall(function, spvArgs); + + // 4. Copy back out an "out" arguments. + lValueCount = 0; + for (int a = 0; a < (int)glslangArgs.size(); ++a) { + if (qualifiers[a] != glslang::EvqConstReadOnly) { + if (qualifiers[a] == glslang::EvqOut || qualifiers[a] == glslang::EvqInOut) { + spv::Id copy = builder.createLoad(spvArgs[a]); + builder.setAccessChain(lValues[lValueCount]); + builder.accessChainStore(copy); + } + ++lValueCount; + } + } + + return result; +} + +spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, spv::Decoration precision, + spv::Id typeId, spv::Id left, spv::Id right, + glslang::TBasicType typeProxy, bool reduceComparison) +{ + bool isUnsigned = typeProxy == glslang::EbtUint; + bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble; + + spv::OpCode binOp = spv::OpNop; + bool needsPromotion = true; + bool comparison = false; + + switch (op) { + case glslang::EOpAdd: + case glslang::EOpAddAssign: + if (isFloat) + binOp = spv::OpFAdd; + else + binOp = spv::OpIAdd; + break; + case glslang::EOpSub: + case glslang::EOpSubAssign: + if (isFloat) + binOp = spv::OpFSub; + else + binOp = spv::OpISub; + break; + case glslang::EOpMul: + case glslang::EOpMulAssign: + if (isFloat) + binOp = spv::OpFMul; + else + binOp = spv::OpIMul; + break; + case glslang::EOpVectorTimesScalar: + case glslang::EOpVectorTimesScalarAssign: + binOp = spv::OpVectorTimesScalar; + needsPromotion = false; + break; + case glslang::EOpVectorTimesMatrix: + case glslang::EOpVectorTimesMatrixAssign: + binOp = spv::OpVectorTimesMatrix; + break; + case glslang::EOpMatrixTimesVector: + binOp = spv::OpMatrixTimesVector; + break; + case glslang::EOpMatrixTimesScalar: + case glslang::EOpMatrixTimesScalarAssign: + binOp = spv::OpMatrixTimesScalar; + break; + case glslang::EOpMatrixTimesMatrix: + case glslang::EOpMatrixTimesMatrixAssign: + binOp = spv::OpMatrixTimesMatrix; + break; + case glslang::EOpOuterProduct: + binOp = spv::OpOuterProduct; + needsPromotion = false; + break; + + case glslang::EOpDiv: + case glslang::EOpDivAssign: + if (isFloat) + binOp = spv::OpFDiv; + else if (isUnsigned) + binOp = spv::OpUDiv; + else + binOp = spv::OpSDiv; + break; + case glslang::EOpMod: + case glslang::EOpModAssign: + if (isFloat) + binOp = spv::OpFMod; + else if (isUnsigned) + binOp = spv::OpUMod; + else + binOp = spv::OpSMod; + break; + case glslang::EOpRightShift: + case glslang::EOpRightShiftAssign: + if (isUnsigned) + binOp = spv::OpShiftRightLogical; + else + binOp = spv::OpShiftRightArithmetic; + break; + case glslang::EOpLeftShift: + case glslang::EOpLeftShiftAssign: + binOp = spv::OpShiftLeftLogical; + break; + case glslang::EOpAnd: + case glslang::EOpAndAssign: + binOp = spv::OpBitwiseAnd; + break; + case glslang::EOpLogicalAnd: + needsPromotion = false; + binOp = spv::OpLogicalAnd; + break; + case glslang::EOpInclusiveOr: + case glslang::EOpInclusiveOrAssign: + binOp = spv::OpBitwiseOr; + break; + case glslang::EOpLogicalOr: + needsPromotion = false; + binOp = spv::OpLogicalOr; + break; + case glslang::EOpExclusiveOr: + case glslang::EOpExclusiveOrAssign: + binOp = spv::OpBitwiseXor; + break; + case glslang::EOpLogicalXor: + needsPromotion = false; + binOp = spv::OpLogicalXor; + break; + + case glslang::EOpLessThan: + case glslang::EOpGreaterThan: + case glslang::EOpLessThanEqual: + case glslang::EOpGreaterThanEqual: + case glslang::EOpEqual: + case glslang::EOpNotEqual: + case glslang::EOpVectorEqual: + case glslang::EOpVectorNotEqual: + comparison = true; + break; + default: + break; + } + + if (binOp != spv::OpNop) { + if (builder.isMatrix(left) || builder.isMatrix(right)) { + switch (binOp) { + case spv::OpMatrixTimesScalar: + case spv::OpVectorTimesMatrix: + case spv::OpMatrixTimesVector: + case spv::OpMatrixTimesMatrix: + break; + case spv::OpFDiv: + // turn it into a multiply... + assert(builder.isMatrix(left) && builder.isScalar(right)); + right = builder.createBinOp(spv::OpFDiv, builder.getTypeId(right), builder.makeFloatConstant(1.0F), right); + binOp = spv::OpFMul; + break; + default: + spv::MissingFunctionality("binary operation on matrix"); + break; + } + + spv::Id id = builder.createBinOp(binOp, typeId, left, right); + builder.setPrecision(id, precision); + + return id; + } + + // No matrix involved; make both operands be the same number of components, if needed + if (needsPromotion) + builder.promoteScalar(precision, left, right); + + spv::Id id = builder.createBinOp(binOp, typeId, left, right); + builder.setPrecision(id, precision); + + return id; + } + + if (! comparison) + return 0; + + // Comparison instructions + + if (reduceComparison && (builder.isVector(left) || builder.isMatrix(left) || builder.isAggregate(left))) { + assert(op == glslang::EOpEqual || op == glslang::EOpNotEqual); + + return builder.createCompare(precision, left, right, op == glslang::EOpEqual); + } + + switch (op) { + case glslang::EOpLessThan: + if (isFloat) + binOp = spv::OpFOrdLessThan; + else if (isUnsigned) + binOp = spv::OpULessThan; + else + binOp = spv::OpSLessThan; + break; + case glslang::EOpGreaterThan: + if (isFloat) + binOp = spv::OpFOrdGreaterThan; + else if (isUnsigned) + binOp = spv::OpUGreaterThan; + else + binOp = spv::OpSGreaterThan; + break; + case glslang::EOpLessThanEqual: + if (isFloat) + binOp = spv::OpFOrdLessThanEqual; + else if (isUnsigned) + binOp = spv::OpULessThanEqual; + else + binOp = spv::OpSLessThanEqual; + break; + case glslang::EOpGreaterThanEqual: + if (isFloat) + binOp = spv::OpFOrdGreaterThanEqual; + else if (isUnsigned) + binOp = spv::OpUGreaterThanEqual; + else + binOp = spv::OpSGreaterThanEqual; + break; + case glslang::EOpEqual: + case glslang::EOpVectorEqual: + if (isFloat) + binOp = spv::OpFOrdEqual; + else + binOp = spv::OpIEqual; + break; + case glslang::EOpNotEqual: + case glslang::EOpVectorNotEqual: + if (isFloat) + binOp = spv::OpFOrdNotEqual; + else + binOp = spv::OpINotEqual; + break; + default: + break; + } + + if (binOp != spv::OpNop) { + spv::Id id = builder.createBinOp(binOp, typeId, left, right); + builder.setPrecision(id, precision); + + return id; + } + + return 0; +} + +spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id operand, bool isFloat) +{ + spv::OpCode unaryOp = spv::OpNop; + int libCall = -1; + + switch (op) { + case glslang::EOpNegative: + if (isFloat) + unaryOp = spv::OpFNegate; + else + unaryOp = spv::OpSNegate; + break; + + case glslang::EOpLogicalNot: + case glslang::EOpVectorLogicalNot: + case glslang::EOpBitwiseNot: + unaryOp = spv::OpNot; + break; + + case glslang::EOpDeterminant: + libCall = GLSL_STD_450::Determinant; + break; + case glslang::EOpMatrixInverse: + libCall = GLSL_STD_450::MatrixInverse; + break; + case glslang::EOpTranspose: + unaryOp = spv::OpTranspose; + break; + + case glslang::EOpRadians: + libCall = GLSL_STD_450::Radians; + break; + case glslang::EOpDegrees: + libCall = GLSL_STD_450::Degrees; + break; + case glslang::EOpSin: + libCall = GLSL_STD_450::Sin; + break; + case glslang::EOpCos: + libCall = GLSL_STD_450::Cos; + break; + case glslang::EOpTan: + libCall = GLSL_STD_450::Tan; + break; + case glslang::EOpAcos: + libCall = GLSL_STD_450::Acos; + break; + case glslang::EOpAsin: + libCall = GLSL_STD_450::Asin; + break; + case glslang::EOpAtan: + libCall = GLSL_STD_450::Atan; + break; + + case glslang::EOpAcosh: + libCall = GLSL_STD_450::Acosh; + break; + case glslang::EOpAsinh: + libCall = GLSL_STD_450::Asinh; + break; + case glslang::EOpAtanh: + libCall = GLSL_STD_450::Atanh; + break; + case glslang::EOpTanh: + libCall = GLSL_STD_450::Tanh; + break; + case glslang::EOpCosh: + libCall = GLSL_STD_450::Cosh; + break; + case glslang::EOpSinh: + libCall = GLSL_STD_450::Sinh; + break; + + case glslang::EOpLength: + libCall = GLSL_STD_450::Length; + break; + case glslang::EOpNormalize: + libCall = GLSL_STD_450::Normalize; + break; + + case glslang::EOpExp: + libCall = GLSL_STD_450::Exp; + break; + case glslang::EOpLog: + libCall = GLSL_STD_450::Log; + break; + case glslang::EOpExp2: + libCall = GLSL_STD_450::Exp2; + break; + case glslang::EOpLog2: + libCall = GLSL_STD_450::Log2; + break; + case glslang::EOpSqrt: + libCall = GLSL_STD_450::Sqrt; + break; + case glslang::EOpInverseSqrt: + libCall = GLSL_STD_450::InverseSqrt; + break; + + case glslang::EOpFloor: + libCall = GLSL_STD_450::Floor; + break; + case glslang::EOpTrunc: + libCall = GLSL_STD_450::Trunc; + break; + case glslang::EOpRound: + libCall = GLSL_STD_450::Round; + break; + case glslang::EOpRoundEven: + libCall = GLSL_STD_450::RoundEven; + break; + case glslang::EOpCeil: + libCall = GLSL_STD_450::Ceil; + break; + case glslang::EOpFract: + libCall = GLSL_STD_450::Fract; + break; + + case glslang::EOpIsNan: + unaryOp = spv::OpIsNan; + break; + case glslang::EOpIsInf: + unaryOp = spv::OpIsInf; + break; + + case glslang::EOpFloatBitsToInt: + libCall = GLSL_STD_450::FloatBitsToInt; + break; + case glslang::EOpFloatBitsToUint: + libCall = GLSL_STD_450::FloatBitsToUint; + break; + case glslang::EOpIntBitsToFloat: + libCall = GLSL_STD_450::IntBitsToFloat; + break; + case glslang::EOpUintBitsToFloat: + libCall = GLSL_STD_450::UintBitsToFloat; + break; + case glslang::EOpPackSnorm2x16: + libCall = GLSL_STD_450::PackSnorm2x16; + break; + case glslang::EOpUnpackSnorm2x16: + libCall = GLSL_STD_450::UnpackSnorm2x16; + break; + case glslang::EOpPackUnorm2x16: + libCall = GLSL_STD_450::PackUnorm2x16; + break; + case glslang::EOpUnpackUnorm2x16: + libCall = GLSL_STD_450::UnpackUnorm2x16; + break; + case glslang::EOpPackHalf2x16: + libCall = GLSL_STD_450::PackHalf2x16; + break; + case glslang::EOpUnpackHalf2x16: + libCall = GLSL_STD_450::UnpackHalf2x16; + break; + + case glslang::EOpDPdx: + unaryOp = spv::OpDPdx; + break; + case glslang::EOpDPdy: + unaryOp = spv::OpDPdy; + break; + case glslang::EOpFwidth: + unaryOp = spv::OpFwidth; + break; + case glslang::EOpDPdxFine: + unaryOp = spv::OpDPdxFine; + break; + case glslang::EOpDPdyFine: + unaryOp = spv::OpDPdyFine; + break; + case glslang::EOpFwidthFine: + unaryOp = spv::OpFwidthFine; + break; + case glslang::EOpDPdxCoarse: + unaryOp = spv::OpDPdxCoarse; + break; + case glslang::EOpDPdyCoarse: + unaryOp = spv::OpDPdyCoarse; + break; + case glslang::EOpFwidthCoarse: + unaryOp = spv::OpFwidthCoarse; + break; + + case glslang::EOpAny: + unaryOp = spv::OpAny; + break; + case glslang::EOpAll: + unaryOp = spv::OpAll; + break; + + case glslang::EOpAbs: + libCall = GLSL_STD_450::Abs; + break; + case glslang::EOpSign: + libCall = GLSL_STD_450::Sign; + break; + + case glslang::EOpEmitStreamVertex: + unaryOp = spv::OpEmitStreamVertex; + break; + case glslang::EOpEndStreamPrimitive: + unaryOp = spv::OpEndStreamPrimitive; + break; + + default: + return 0; + } + + spv::Id id; + if (libCall >= 0) { + std::vector args; + args.push_back(operand); + id = builder.createBuiltinCall(precision, typeId, stdBuiltins, libCall, args); + } else + id = builder.createUnaryOp(unaryOp, typeId, operand); + + builder.setPrecision(id, precision); + + return id; +} + +spv::Id TGlslangToSpvTraverser::createConversion(glslang::TOperator op, spv::Decoration precision, spv::Id destType, spv::Id operand) +{ + spv::OpCode convOp = spv::OpNop; + spv::Id zero; + spv::Id one; + + int vectorSize = builder.isVectorType(destType) ? builder.getNumTypeComponents(destType) : 0; + + switch (op) { + case glslang::EOpConvIntToBool: + case glslang::EOpConvUintToBool: + zero = builder.makeUintConstant(0); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpINotEqual, destType, operand, zero); + + case glslang::EOpConvFloatToBool: + zero = builder.makeFloatConstant(0.0F); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero); + + case glslang::EOpConvDoubleToBool: + zero = builder.makeDoubleConstant(0.0); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero); + + case glslang::EOpConvBoolToFloat: + convOp = spv::OpSelect; + zero = builder.makeFloatConstant(0.0); + one = builder.makeFloatConstant(1.0); + break; + case glslang::EOpConvBoolToDouble: + convOp = spv::OpSelect; + zero = builder.makeDoubleConstant(0.0); + one = builder.makeDoubleConstant(1.0); + break; + case glslang::EOpConvBoolToInt: + zero = builder.makeIntConstant(0); + one = builder.makeIntConstant(1); + convOp = spv::OpSelect; + break; + case glslang::EOpConvBoolToUint: + zero = builder.makeUintConstant(0); + one = builder.makeUintConstant(1); + convOp = spv::OpSelect; + break; + + case glslang::EOpConvIntToFloat: + case glslang::EOpConvIntToDouble: + convOp = spv::OpConvertSToF; + break; + + case glslang::EOpConvUintToFloat: + case glslang::EOpConvUintToDouble: + convOp = spv::OpConvertUToF; + break; + + case glslang::EOpConvDoubleToFloat: + case glslang::EOpConvFloatToDouble: + convOp = spv::OpFConvert; + break; + + case glslang::EOpConvFloatToInt: + case glslang::EOpConvDoubleToInt: + convOp = spv::OpConvertFToS; + break; + + case glslang::EOpConvUintToInt: + case glslang::EOpConvIntToUint: + convOp = spv::OpBitcast; + break; + + case glslang::EOpConvFloatToUint: + case glslang::EOpConvDoubleToUint: + convOp = spv::OpConvertFToU; + break; + default: + break; + } + + spv::Id result = 0; + if (convOp == spv::OpNop) + return result; + + if (convOp == spv::OpSelect) { + zero = makeSmearedConstant(zero, vectorSize); + one = makeSmearedConstant(one, vectorSize); + result = builder.createTriOp(convOp, destType, operand, one, zero); + } else + result = builder.createUnaryOp(convOp, destType, operand); + + builder.setPrecision(result, precision); + + return result; +} + +spv::Id TGlslangToSpvTraverser::makeSmearedConstant(spv::Id constant, int vectorSize) +{ + if (vectorSize == 0) + return constant; + + spv::Id vectorTypeId = builder.makeVectorType(builder.getTypeId(constant), vectorSize); + std::vector components; + for (int c = 0; c < vectorSize; ++c) + components.push_back(constant); + return builder.makeCompositeConstant(vectorTypeId, components); +} + +spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector& operands, bool isUnsigned) +{ + spv::OpCode opCode = spv::OpNop; + int libCall = -1; + + switch (op) { + case glslang::EOpMin: + libCall = GLSL_STD_450::Min; + break; + case glslang::EOpModf: + libCall = GLSL_STD_450::Modf; + break; + case glslang::EOpMax: + libCall = GLSL_STD_450::Max; + break; + case glslang::EOpPow: + libCall = GLSL_STD_450::Pow; + break; + case glslang::EOpDot: + opCode = spv::OpDot; + break; + case glslang::EOpAtan: + libCall = GLSL_STD_450::Atan2; + break; + + case glslang::EOpClamp: + libCall = GLSL_STD_450::Clamp; + break; + case glslang::EOpMix: + libCall = GLSL_STD_450::Mix; + break; + case glslang::EOpStep: + libCall = GLSL_STD_450::Step; + break; + case glslang::EOpSmoothStep: + libCall = GLSL_STD_450::SmoothStep; + break; + + case glslang::EOpDistance: + libCall = GLSL_STD_450::Distance; + break; + case glslang::EOpCross: + libCall = GLSL_STD_450::Cross; + break; + case glslang::EOpFaceForward: + libCall = GLSL_STD_450::FaceForward; + break; + case glslang::EOpReflect: + libCall = GLSL_STD_450::Reflect; + break; + case glslang::EOpRefract: + libCall = GLSL_STD_450::Refract; + break; + default: + return 0; + } + + spv::Id id = 0; + if (libCall >= 0) + id = builder.createBuiltinCall(precision, typeId, stdBuiltins, libCall, operands); + else { + switch (operands.size()) { + case 0: + id = builder.createEmptyOp(opCode); + break; + case 1: + // should all be handled by createUnaryOperation + assert(0); + break; + case 2: + id = builder.createBinOp(opCode, typeId, operands[0], operands[1]); + break; + case 3: + id = builder.createTernaryOp(opCode, typeId, operands[0], operands[1], operands[2]); + break; + default: + // These do not exist yet + assert(0 && "operation with more than 3 operands"); + break; + } + } + + builder.setPrecision(id, precision); + + return id; +} + +// Intrinsics with no arguments, no return value, and no precision. +spv::Id TGlslangToSpvTraverser::createNoArgOperation(glslang::TOperator op) +{ + // TODO: get the barrier operands correct + + switch (op) { + case glslang::EOpEmitVertex: + return builder.createEmptyOp(spv::OpEmitVertex); + case glslang::EOpEndPrimitive: + return builder.createEmptyOp(spv::OpEndPrimitive); + case glslang::EOpBarrier: + builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsAllMemory); + builder.createControlBarrier(spv::ExecutionScopeDevice); + return 0; + case glslang::EOpMemoryBarrier: + builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsAllMemory); + return 0; + case glslang::EOpMemoryBarrierAtomicCounter: + builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsAtomicCounter); + return 0; + case glslang::EOpMemoryBarrierBuffer: + builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsUniform); + return 0; + case glslang::EOpMemoryBarrierImage: + builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsImage); + return 0; + case glslang::EOpMemoryBarrierShared: + builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsWorkgroupLocal); + return 0; + case glslang::EOpGroupMemoryBarrier: + builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsWorkgroupGlobal); + return 0; + default: + spv::MissingFunctionality("operation with no arguments"); + return 0; + } +} + +spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol) +{ + std::map::iterator iter; + iter = symbolValues.find(symbol->getId()); + spv::Id id; + if (symbolValues.end() != iter) { + id = iter->second; + return id; + } + + // it was not found, create it + id = createSpvVariable(symbol); + symbolValues[symbol->getId()] = id; + + if (! symbol->getType().isStruct()) { + addDecoration(id, TranslatePrecisionDecoration(symbol->getType())); + addDecoration(id, TranslateInterpolationDecoration(symbol->getType())); + if (symbol->getQualifier().hasLocation()) + builder.addDecoration(id, spv::DecLocation, symbol->getQualifier().layoutLocation); + if (symbol->getQualifier().hasComponent()) + builder.addDecoration(id, spv::DecComponent, symbol->getQualifier().layoutComponent); + if (glslangIntermediate->getXfbMode()) { + if (symbol->getQualifier().hasXfbStride()) + builder.addDecoration(id, spv::DecStride, symbol->getQualifier().layoutXfbStride); + if (symbol->getQualifier().hasXfbBuffer()) + builder.addDecoration(id, spv::DecXfbBuffer, symbol->getQualifier().layoutXfbBuffer); + if (symbol->getQualifier().hasXfbOffset()) + builder.addDecoration(id, spv::DecOffset, symbol->getQualifier().layoutXfbOffset); + } + } + + addDecoration(id, TranslateInvariantDecoration(symbol->getType())); + if (symbol->getQualifier().hasStream()) + builder.addDecoration(id, spv::DecStream, symbol->getQualifier().layoutStream); + if (symbol->getQualifier().hasSet()) + builder.addDecoration(id, spv::DecDescriptorSet, symbol->getQualifier().layoutSet); + if (symbol->getQualifier().hasBinding()) + builder.addDecoration(id, spv::DecBinding, symbol->getQualifier().layoutBinding); + if (glslangIntermediate->getXfbMode()) { + if (symbol->getQualifier().hasXfbStride()) + builder.addDecoration(id, spv::DecStride, symbol->getQualifier().layoutXfbStride); + if (symbol->getQualifier().hasXfbBuffer()) + builder.addDecoration(id, spv::DecXfbBuffer, symbol->getQualifier().layoutXfbBuffer); + } + + // built-in variable decorations + int num = TranslateBuiltInDecoration(*symbol); + if (num >= 0) + builder.addDecoration(id, spv::DecBuiltIn, num); + + if (linkageOnly) + builder.addDecoration(id, spv::DecNoStaticUse); + + return id; +} + +void TGlslangToSpvTraverser::addDecoration(spv::Id id, spv::Decoration dec) +{ + if (dec != spv::DecCount) + builder.addDecoration(id, dec); +} + +void TGlslangToSpvTraverser::addMemberDecoration(spv::Id id, int member, spv::Decoration dec) +{ + if (dec != spv::DecCount) + builder.addMemberDecoration(id, (unsigned)member, dec); +} + +// Use 'consts' as the flattened glslang source of scalar constants to recursively +// build the aggregate SPIR-V constant. +// +// If there are not enough elements present in 'consts', 0 will be substituted; +// an empty 'consts' can be used to create a fully zeroed SPIR-V constant. +// +spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangType, const glslang::TConstUnionArray& consts, int& nextConst) +{ + // vector of constants for SPIR-V + std::vector spvConsts; + + // Type is used for struct and array constants + spv::Id typeId = convertGlslangToSpvType(glslangType); + + if (glslangType.isArray()) { + glslang::TType elementType; + elementType.shallowCopy(glslangType); // TODO: desktop arrays of arrays functionality will need a deeper copy to avoid modifying the original + elementType.dereference(); + for (int i = 0; i < glslangType.getArraySize(); ++i) + spvConsts.push_back(createSpvConstant(elementType, consts, nextConst)); + } else if (glslangType.isMatrix()) { + glslang::TType vectorType; + vectorType.shallowCopy(glslangType); + vectorType.dereference(); + for (int col = 0; col < glslangType.getMatrixCols(); ++col) + spvConsts.push_back(createSpvConstant(vectorType, consts, nextConst)); + } else if (glslangType.getStruct()) { + glslang::TVector::const_iterator iter; + for (iter = glslangType.getStruct()->begin(); iter != glslangType.getStruct()->end(); ++iter) + spvConsts.push_back(createSpvConstant(*iter->type, consts, nextConst)); + } else if (glslangType.isVector()) { + for (unsigned int i = 0; i < (unsigned int)glslangType.getVectorSize(); ++i) { + bool zero = nextConst >= consts.size(); + switch (glslangType.getBasicType()) { + case glslang::EbtInt: + spvConsts.push_back(builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst())); + break; + case glslang::EbtUint: + spvConsts.push_back(builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst())); + break; + case glslang::EbtFloat: + spvConsts.push_back(builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst())); + break; + case glslang::EbtDouble: + spvConsts.push_back(builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst())); + break; + case glslang::EbtBool: + spvConsts.push_back(builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst())); + break; + default: + spv::MissingFunctionality("constant vector type"); + break; + } + ++nextConst; + } + } else { + // we have a non-aggregate (scalar) constant + bool zero = nextConst >= consts.size(); + spv::Id scalar; + switch (glslangType.getBasicType()) { + case glslang::EbtInt: + scalar = builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst()); + break; + case glslang::EbtUint: + scalar = builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst()); + break; + case glslang::EbtFloat: + scalar = builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst()); + break; + case glslang::EbtDouble: + scalar = builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst()); + break; + case glslang::EbtBool: + scalar = builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst()); + break; + default: + spv::MissingFunctionality("constant scalar type"); + break; + } + ++nextConst; + return scalar; + } + + return builder.makeCompositeConstant(typeId, spvConsts); +} + +}; // end anonymous namespace + +namespace glslang { + +// Write SPIR-V out to a binary file +void OutputSpv(const std::vector& spirv, const char* baseName) +{ + std::ofstream out; + std::string fileName(baseName); + fileName.append(".spv"); + out.open(fileName.c_str(), std::ios::binary | std::ios::out); + for (int i = 0; i < (int)spirv.size(); ++i) { + unsigned int word = spirv[i]; + out.write((const char*)&word, 4); + } + out.close(); +} + +// +// Set up the glslang traversal +// +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv) +{ + TIntermNode* root = intermediate.getTreeRoot(); + + if (root == 0) + return; + + glslang::GetThreadPoolAllocator().push(); + + TGlslangToSpvTraverser it(&intermediate); + + root->traverse(&it); + + it.dumpSpv(spirv); + + glslang::GetThreadPoolAllocator().pop(); +} + +}; // end namespace glslang diff --git a/BIL/GlslangToBil.h b/SPIRV/GlslangToSpv.h similarity index 88% rename from BIL/GlslangToBil.h rename to SPIRV/GlslangToSpv.h index 025bb08a..1a14fe55 100644 --- a/BIL/GlslangToBil.h +++ b/SPIRV/GlslangToSpv.h @@ -36,8 +36,7 @@ namespace glslang { -void GlslangToBil(const glslang::TIntermediate& intermediate, std::vector bil); - -void OutputBil(const std::vector& bil, const char* baseName); +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv); +void OutputSpv(const std::vector& spirv, const char* baseName); }; diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp new file mode 100644 index 00000000..74212840 --- /dev/null +++ b/SPIRV/SpvBuilder.cpp @@ -0,0 +1,2011 @@ +// +//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 +#include +#include + +#include "SpvBuilder.h" + +#ifndef _WIN32 + #include +#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& 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& 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& 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& 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 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& 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& 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& 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& 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& 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& 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& 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& 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(value1->getType()); + //if (arrayType) + // numElements = (int)arrayType->getNumElements(); + //else { + // // better be structure + // const llvm::StructType* structType = llvm::dyn_cast(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& 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& 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 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 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& 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 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 matrixColumns; + for (int col = 0; col < numCols; ++col) { + std::vector 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& caseValues, std::vector& valueIndexToSegment, int defaultSegment, + std::vector& 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& 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& 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& swizzle, int width, Id type) +{ + // if needed, propagate the swizzle for the current access chain + if (accessChain.swizzle.size()) { + std::vector 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 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& 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& out, const std::vector& 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 diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h new file mode 100644 index 00000000..f2b746c0 --- /dev/null +++ b/SPIRV/SpvBuilder.h @@ -0,0 +1,519 @@ +// +//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 +#include +#include + +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 for anything needing a new one. + Id getUniqueId() { return ++uniqueId; } + + // To get a set of new 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& 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& 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& 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& 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& offsets); + + // Create an OpCompositeExtract instruction + Id createCompositeExtract(Id composite, Id typeId, unsigned index); + Id createCompositeExtract(Id composite, Id typeId, std::vector& indexes); + Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index); + Id createCompositeInsert(Id object, Id composite, Id typeId, std::vector& 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&); + + // 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& 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& 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& 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& constituents); + + // vector or scalar constructor + Id createConstructor(Decoration precision, const std::vector& sources, Id resultTypeId); + + // matrix constructor + Id createMatrixConstructor(Decoration precision, const std::vector& 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& caseValues, std::vector& valueToSegment, int defaultSegment, + std::vector& 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& segmentBB, int segment); + + // Finish off the innermost switch. + void endSwitch(std::vector& 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 indexChain; + Id instr; // the instruction that generates this access chain + std::vector 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& 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&) const; + +protected: + Id findScalarConstant(OpCode typeClass, Id typeId, unsigned value) const; + Id findCompositeConstant(OpCode typeClass, std::vector& 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&, const std::vector&) const; + + SourceLanguage source; + int sourceVersion; + std::vector 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 imports; + std::vector entryPoints; + std::vector executionModes; + std::vector names; + std::vector lines; + std::vector decorations; + std::vector constantsTypesGlobals; + std::vector externals; + + // not output, internally used for quick & dirty canonical (unique) creation + std::vector groupedConstants[OpConstant]; // all types appear before OpConstant + std::vector groupedTypes[OpConstant]; + + // stack of switches + std::stack 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 loops; +}; // end Builder class + +void MissingFunctionality(const char*); +void ValidationError(const char* error); + +}; // end spv namespace + +#endif // SpvBuilder_H diff --git a/SPIRV/spirv.h b/SPIRV/spirv.h new file mode 100644 index 00000000..7404d6fd --- /dev/null +++ b/SPIRV/spirv.h @@ -0,0 +1,762 @@ +/* +** 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 s or instruction 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 diff --git a/SPIRV/spvIR.h b/SPIRV/spvIR.h new file mode 100644 index 00000000..5bea5967 --- /dev/null +++ b/SPIRV/spvIR.h @@ -0,0 +1,348 @@ +// +//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 +#include + +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 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& 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 operands; + std::vector* 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& 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 instructions; + std::vector predecessors; + std::vector 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& 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 parameterInstructions; + std::vector 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& out) const + { + for (int f = 0; f < (int)functions.size(); ++f) + functions[f]->dump(out); + } + +protected: + Module(const Module&); + std::vector functions; + + // map from result id to instruction having that result id + std::vector 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 diff --git a/StandAlone/CMakeLists.txt b/StandAlone/CMakeLists.txt index ec4ba666..6d291075 100644 --- a/StandAlone/CMakeLists.txt +++ b/StandAlone/CMakeLists.txt @@ -17,7 +17,7 @@ set(LIBRARIES glslang OGLCompiler OSDependent - BIL) + SPIRV) if(WIN32) set(LIBRARIES ${LIBRARIES} psapi) diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index c14e12a3..c9f2da0f 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -40,8 +40,8 @@ #include "Worklist.h" #include "./../glslang/Include/ShHandle.h" #include "./../glslang/Public/ShaderLang.h" -#include "../BIL/GlslangToBil.h" -#include "../BIL/GLSL450Lib.h" +#include "../SPIRV/GlslangToSpv.h" +#include "../SPIRV/GLSL450Lib.h" #include #include #include @@ -66,7 +66,7 @@ enum TOptions { EOptionDumpReflection = 0x100, EOptionSuppressWarnings = 0x200, EOptionDumpVersions = 0x400, - EOptionBil = 0x800, + EOptionSpv = 0x800, EOptionDefaultDesktop = 0x1000, }; @@ -479,8 +479,8 @@ bool ProcessArguments(int argc, char* argv[]) Work[argc] = 0; if (argv[0][0] == '-') { switch (argv[0][1]) { - case 'b': - Options |= EOptionBil; + case 'V': + Options |= EOptionSpv; Options |= EOptionLinkProgram; break; case 'c': @@ -634,14 +634,14 @@ void CompileAndLinkShaders() program.dumpReflection(); } - if (Options & EOptionBil) { + if (Options & EOptionSpv) { 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 { for (int stage = 0; stage < EShLangCount; ++stage) { if (program.getIntermediate((EShLanguage)stage)) { - std::vector bil; - glslang::GlslangToBil(*program.getIntermediate((EShLanguage)stage), bil); + std::vector spirv; + glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv); const char* name; switch (stage) { case EShLangVertex: name = "vert"; break; @@ -652,7 +652,7 @@ void CompileAndLinkShaders() case EShLangCompute: name = "comp"; break; default: name = "unknown"; break; } - glslang::OutputBil(bil, name); + glslang::OutputSpv(spirv, name); } } } @@ -839,7 +839,7 @@ void usage() { printf("Usage: glslangValidator [option]... [file]...\n" "\n" - "Where: each 'file' ends in\n" + "Where: each 'file' ends in ., where is one of\n" " .conf to provide an optional config file that replaces the default configuration\n" " (see -c option below for generating a template)\n" " .vert for a vertex shader\n" @@ -853,7 +853,7 @@ void usage() "\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" - " -b create BIL in file .bil\n" + " -V create SPIR-V in file .spv\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" " -i intermediate tree (glslang AST) is printed out\n" diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h index 80ae123f..fe1ffa38 100644 --- a/glslang/Include/Types.h +++ b/glslang/Include/Types.h @@ -482,6 +482,7 @@ public: layoutLocation = layoutLocationEnd; layoutComponent = layoutComponentEnd; + layoutSet = layoutSetEnd; layoutBinding = layoutBindingEnd; layoutIndex = layoutIndexEnd; @@ -513,6 +514,9 @@ public: unsigned int layoutComponent : 3; static const unsigned int layoutComponentEnd = 4; + unsigned int layoutSet : 7; + static const unsigned int layoutSetEnd = 0x3F; + unsigned int layoutBinding : 8; static const unsigned int layoutBindingEnd = 0xFF; @@ -575,6 +579,10 @@ public: { return layoutIndex != layoutIndexEnd; } + bool hasSet() const + { + return layoutSet != layoutSetEnd; + } bool hasBinding() const { return layoutBinding != layoutBindingEnd; @@ -1201,6 +1209,8 @@ public: if (qualifier.hasIndex()) p += snprintf(p, end - p, "index=%d ", qualifier.layoutIndex); } + if (qualifier.hasSet()) + p += snprintf(p, end - p, "set=%d ", qualifier.layoutSet); if (qualifier.hasBinding()) p += snprintf(p, end - p, "binding=%d ", qualifier.layoutBinding); if (qualifier.hasStream()) diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index e150c507..9e695ba6 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -3312,6 +3312,12 @@ void TParseContext::setLayoutQualifier(TSourceLoc loc, TPublicType& publicType, else publicType.qualifier.layoutLocation = value; 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") { profileRequires(loc, ~EEsProfile, 420, GL_ARB_shading_language_420pack, "binding"); profileRequires(loc, EEsProfile, 310, 0, "binding"); @@ -3476,6 +3482,8 @@ void TParseContext::mergeObjectLayoutQualifiers(TSourceLoc loc, TQualifier& dst, if (src.hasOffset()) dst.layoutOffset = src.layoutOffset; + if (src.hasSet()) + dst.layoutSet = src.layoutSet; if (src.layoutBinding != TQualifier::layoutBindingEnd) dst.layoutBinding = src.layoutBinding;