From 24e4bc99ace270c4f81b67591b5b7ee56b2da384 Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Wed, 9 Mar 2016 14:53:57 -0500 Subject: [PATCH 01/16] Fix array out of bounds bug in processing if-else-endif macros. If we are not inside an if macro, we cannot simply decrease elsetracker. Fixes https://github.com/KhronosGroup/glslang/issues/29. --- Test/baseResults/preprocessor.many.endif.vert.err | 12 ++++++++++++ Test/baseResults/preprocessor.many.endif.vert.out | 0 Test/preprocessor.many.endif.vert | 10 ++++++++++ Test/test-preprocessor-list | 1 + glslang/MachineIndependent/preprocessor/Pp.cpp | 7 ++++--- 5 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 Test/baseResults/preprocessor.many.endif.vert.err create mode 100644 Test/baseResults/preprocessor.many.endif.vert.out create mode 100644 Test/preprocessor.many.endif.vert diff --git a/Test/baseResults/preprocessor.many.endif.vert.err b/Test/baseResults/preprocessor.many.endif.vert.err new file mode 100644 index 00000000..49aafc5a --- /dev/null +++ b/Test/baseResults/preprocessor.many.endif.vert.err @@ -0,0 +1,12 @@ +ERROR: 0:1: '#endif' : mismatched statements +ERROR: 0:2: '#endif' : mismatched statements +ERROR: 0:3: '#endif' : mismatched statements +ERROR: 0:4: '#endif' : mismatched statements +ERROR: 0:5: '#endif' : mismatched statements +ERROR: 0:6: '#endif' : mismatched statements +ERROR: 0:7: '#endif' : mismatched statements +ERROR: 0:10: 'preprocessor evaluation' : bad expression +ERROR: 0:11: '' : missing #endif +ERROR: 9 compilation errors. No code generated. + + diff --git a/Test/baseResults/preprocessor.many.endif.vert.out b/Test/baseResults/preprocessor.many.endif.vert.out new file mode 100644 index 00000000..e69de29b diff --git a/Test/preprocessor.many.endif.vert b/Test/preprocessor.many.endif.vert new file mode 100644 index 00000000..7b049629 --- /dev/null +++ b/Test/preprocessor.many.endif.vert @@ -0,0 +1,10 @@ +#endif +#endif +#endif +#endif +#endif +#endif +#endif + +#if +#else diff --git a/Test/test-preprocessor-list b/Test/test-preprocessor-list index ac8f8be8..80d4b225 100644 --- a/Test/test-preprocessor-list +++ b/Test/test-preprocessor-list @@ -12,3 +12,4 @@ preprocessor.pragma.vert preprocessor.simple.vert preprocessor.success_if_parse_would_fail.vert preprocessor.defined.vert +preprocessor.many.endif.vert diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp index 9a33a776..da709421 100644 --- a/glslang/MachineIndependent/preprocessor/Pp.cpp +++ b/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -867,12 +867,13 @@ int TPpContext::readCPPline(TPpToken* ppToken) token = CPPelse(0, ppToken); break; case PpAtomEndif: - elseSeen[elsetracker] = false; - --elsetracker; if (! ifdepth) parseContext.ppError(ppToken->loc, "mismatched statements", "#endif", ""); - else + else { + elseSeen[elsetracker] = false; + --elsetracker; --ifdepth; + } token = extraTokenCheck(PpAtomEndif, ppToken, scanToken(ppToken)); break; case PpAtomIf: From da39733f2892d3e65af8e1286fbb348cfc2883fd Mon Sep 17 00:00:00 2001 From: qining Date: Wed, 9 Mar 2016 19:54:03 -0500 Subject: [PATCH 02/16] Remove decoration of undefined IDs Fix issue #185 by removing OpDecorate instructions whose target IDs are defined in unreachable blocks and thus not dumped in the generated SPIR-V code. --- SPIRV/GlslangToSpv.cpp | 1 + SPIRV/SpvBuilder.cpp | 29 +++++++++++++++++++++++++++++ SPIRV/SpvBuilder.h | 3 +++ SPIRV/spvIR.h | 5 +++++ 4 files changed, 38 insertions(+) diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 1a8ba449..8c1c84f9 100755 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -719,6 +719,7 @@ void TGlslangToSpvTraverser::dumpSpv(std::vector& out) for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it) entryPoint->addIdOperand(*it); + builder.eliminateDeadDecorations(); builder.dump(out); } diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp index d7f6fc7b..158c86b9 100644 --- a/SPIRV/SpvBuilder.cpp +++ b/SPIRV/SpvBuilder.cpp @@ -2130,6 +2130,35 @@ Id Builder::accessChainGetInferredType() return type; } +// comment in header +void Builder::eliminateDeadDecorations() { + std::unordered_set reachable_blocks; + std::unordered_set unreachable_definitions; + // Collect IDs defined in unreachable blocks. For each function, label the + // reachable blocks first. Then for each unreachable block, collect the + // result IDs of the instructions in it. + for (auto& f : module.getFunctions()) { + Block* entry = f->getEntryBlock(); + inReadableOrder(entry, [&reachable_blocks](const Block* b) { + reachable_blocks.insert(b); + }); + for (auto& b : f->getBlocks()) { + if (!reachable_blocks.count(b)) { + for (auto& i : b->getInstructions()) { + unreachable_definitions.insert(i->getResultId()); + } + } + } + } + decorations.erase(std::remove_if(decorations.begin(), decorations.end(), + [&unreachable_definitions](std::unique_ptr& I) { + Instruction* inst = I.get(); + Id decoration_id = inst->getIdOperand(0); + return unreachable_definitions.count(decoration_id) != 0; + }), + decorations.end()); +} + void Builder::dump(std::vector& out) const { // Header, before first instructions: diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h index d6dc6121..e16ba38c 100755 --- a/SPIRV/SpvBuilder.h +++ b/SPIRV/SpvBuilder.h @@ -512,6 +512,9 @@ public: // based on the type of the base and the chain of dereferences. Id accessChainGetInferredType(); + // Remove OpDecorate instructions whose operands are defined in unreachable + // blocks. + void eliminateDeadDecorations(); void dump(std::vector&) const; void createBranch(Block* block); diff --git a/SPIRV/spvIR.h b/SPIRV/spvIR.h index 98f4971b..3c483872 100755 --- a/SPIRV/spvIR.h +++ b/SPIRV/spvIR.h @@ -182,6 +182,9 @@ public: void addLocalVariable(std::unique_ptr inst) { localVariables.push_back(std::move(inst)); } const std::vector& getPredecessors() const { return predecessors; } const std::vector& getSuccessors() const { return successors; } + const std::vector >& getInstructions() const { + return instructions; + } void setUnreachable() { unreachable = true; } bool isUnreachable() const { return unreachable; } // Returns the block's merge instruction, if one exists (otherwise null). @@ -275,6 +278,7 @@ public: Module& getParent() const { return parent; } Block* getEntryBlock() const { return blocks.front(); } Block* getLastBlock() const { return blocks.back(); } + const std::vector& getBlocks() const { return blocks; } void addLocalVariable(std::unique_ptr inst); Id getReturnType() const { return functionInstruction.getTypeId(); } void dump(std::vector& out) const @@ -326,6 +330,7 @@ public: } Instruction* getInstruction(Id id) const { return idToInstruction[id]; } + const std::vector& getFunctions() const { return functions; } spv::Id getTypeId(Id resultId) const { return idToInstruction[resultId]->getTypeId(); } StorageClass getStorageClass(Id typeId) const { From 95aa52737d6b7053578aba772b7ccc7e80df6d28 Mon Sep 17 00:00:00 2001 From: qining Date: Wed, 9 Mar 2016 21:40:41 -0500 Subject: [PATCH 03/16] Change to traditional 'for' loop --- SPIRV/SpvBuilder.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp index 158c86b9..336d3de3 100644 --- a/SPIRV/SpvBuilder.cpp +++ b/SPIRV/SpvBuilder.cpp @@ -2137,14 +2137,21 @@ void Builder::eliminateDeadDecorations() { // Collect IDs defined in unreachable blocks. For each function, label the // reachable blocks first. Then for each unreachable block, collect the // result IDs of the instructions in it. - for (auto& f : module.getFunctions()) { + for (std::vector::const_iterator fi = module.getFunctions().cbegin(); + fi != module.getFunctions().cend(); fi++) { + Function* f = *fi; Block* entry = f->getEntryBlock(); inReadableOrder(entry, [&reachable_blocks](const Block* b) { reachable_blocks.insert(b); }); - for (auto& b : f->getBlocks()) { + for (std::vector::const_iterator bi = f->getBlocks().cbegin(); + bi != f->getBlocks().cend(); bi++) { + Block* b = *bi; if (!reachable_blocks.count(b)) { - for (auto& i : b->getInstructions()) { + for (std::vector >::const_iterator + ii = b->getInstructions().cbegin(); + ii != b->getInstructions().cend(); ii++) { + Instruction* i = ii->get(); unreachable_definitions.insert(i->getResultId()); } } From 2677d918f1a42b29352c27ce5cef6f6c119c8121 Mon Sep 17 00:00:00 2001 From: Dejan Mircevski Date: Fri, 11 Mar 2016 12:06:17 -0500 Subject: [PATCH 04/16] Test dead-decoration removal. --- .../spv.noDeadDecorations.vert.out | 64 +++++++++++++++++++ Test/spv.noDeadDecorations.vert | 13 ++++ Test/test-spirv-list | 1 + 3 files changed, 78 insertions(+) create mode 100644 Test/baseResults/spv.noDeadDecorations.vert.out create mode 100644 Test/spv.noDeadDecorations.vert diff --git a/Test/baseResults/spv.noDeadDecorations.vert.out b/Test/baseResults/spv.noDeadDecorations.vert.out new file mode 100644 index 00000000..e9b23566 --- /dev/null +++ b/Test/baseResults/spv.noDeadDecorations.vert.out @@ -0,0 +1,64 @@ +spv.noDeadDecorations.vert +Warning, version 310 is not yet complete; most version-specific features are present, but some are missing. + + +Linked vertex stage: + + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 32 + + Capability Shader + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Vertex 4 "main" 22 + Source ESSL 310 + Name 4 "main" + Name 10 "func(f1;" + Name 9 "a" + Name 20 "gl_PerVertex" + MemberName 20(gl_PerVertex) 0 "gl_Position" + MemberName 20(gl_PerVertex) 1 "gl_PointSize" + Name 22 "" + Name 26 "param" + Decorate 10(func(f1;) RelaxedPrecision + Decorate 9(a) RelaxedPrecision + Decorate 12 RelaxedPrecision + Decorate 13 RelaxedPrecision + MemberDecorate 20(gl_PerVertex) 0 BuiltIn Position + MemberDecorate 20(gl_PerVertex) 1 BuiltIn PointSize + Decorate 20(gl_PerVertex) Block + Decorate 27 RelaxedPrecision + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypePointer Function 6(float) + 8: TypeFunction 6(float) 7(ptr) + 16: 6(float) Constant 3212836864 + 19: TypeVector 6(float) 4 +20(gl_PerVertex): TypeStruct 19(fvec4) 6(float) + 21: TypePointer Output 20(gl_PerVertex) + 22: 21(ptr) Variable Output + 23: TypeInt 32 1 + 24: 23(int) Constant 0 + 25: 6(float) Constant 0 + 28: TypeInt 32 0 + 29: 28(int) Constant 0 + 30: TypePointer Output 6(float) + 4(main): 2 Function None 3 + 5: Label + 26(param): 7(ptr) Variable Function + Store 26(param) 25 + 27: 6(float) FunctionCall 10(func(f1;) 26(param) + 31: 30(ptr) AccessChain 22 24 29 + Store 31 27 + Return + FunctionEnd + 10(func(f1;): 6(float) Function None 8 + 9(a): 7(ptr) FunctionParameter + 11: Label + 12: 6(float) Load 9(a) + 13: 6(float) FNegate 12 + ReturnValue 13 + FunctionEnd diff --git a/Test/spv.noDeadDecorations.vert b/Test/spv.noDeadDecorations.vert new file mode 100644 index 00000000..88984970 --- /dev/null +++ b/Test/spv.noDeadDecorations.vert @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; + +float func(float a) +{ + return -a; + a = a * -1.0; +} + +void main() +{ + gl_Position.x = func(0.0); +} diff --git a/Test/test-spirv-list b/Test/test-spirv-list index 009e0453..0e223674 100644 --- a/Test/test-spirv-list +++ b/Test/test-spirv-list @@ -64,6 +64,7 @@ spv.matrix2.frag spv.memoryQualifier.frag spv.merge-unreachable.frag spv.newTexture.frag +spv.noDeadDecorations.vert spv.nonSquare.vert spv.Operations.frag spv.intOps.vert From d715adc0f2328b517666abecb89501e324813dc2 Mon Sep 17 00:00:00 2001 From: Rex Xu Date: Tue, 15 Mar 2016 12:08:31 +0800 Subject: [PATCH 05/16] SPV: Implement boolean selection for mix(). Use OpSelect to implement boolean selection of mix(). FMix is applicable to linear blending of mix(). --- SPIRV/GlslangToSpv.cpp | 7 ++++--- Test/baseResults/spv.400.frag.out | 8 ++++---- Test/baseResults/spv.Operations.frag.out | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 8c1c84f9..9b03b9a7 100755 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -3435,11 +3435,12 @@ spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv:: builder.promoteScalar(precision, operands.front(), operands[2]); break; case glslang::EOpMix: - if (isFloat) + if (! builder.isBoolType(builder.getScalarTypeId(builder.getTypeId(operands.back())))) { + assert(isFloat); libCall = spv::GLSLstd450FMix; - else { + } else { opCode = spv::OpSelect; - spv::MissingFunctionality("translating integer mix to OpSelect"); + std::swap(operands.front(), operands.back()); } builder.promoteScalar(precision, operands.front(), operands.back()); break; diff --git a/Test/baseResults/spv.400.frag.out b/Test/baseResults/spv.400.frag.out index a851500c..0cf2c36e 100644 --- a/Test/baseResults/spv.400.frag.out +++ b/Test/baseResults/spv.400.frag.out @@ -719,28 +719,28 @@ Linked fragment stage: 424: 37(float) Load 39(doublev) 425: 37(float) Load 39(doublev) 429: 426(bool) Load 428(boolv) - 430: 37(float) ExtInst 1(GLSL.std.450) 46(FMix) 424 425 429 + 430: 37(float) Select 429 425 424 431: 37(float) Load 39(doublev) 432: 37(float) FAdd 431 430 Store 39(doublev) 432 433: 41(fvec2) Load 43(dvec2v) 434: 41(fvec2) Load 43(dvec2v) 438: 435(bvec2) Load 437(bvec2v) - 439: 41(fvec2) ExtInst 1(GLSL.std.450) 46(FMix) 433 434 438 + 439: 41(fvec2) Select 438 434 433 440: 41(fvec2) Load 43(dvec2v) 441: 41(fvec2) FAdd 440 439 Store 43(dvec2v) 441 442: 46(fvec3) Load 48(dvec3v) 443: 46(fvec3) Load 48(dvec3v) 447: 444(bvec3) Load 446(bvec3v) - 448: 46(fvec3) ExtInst 1(GLSL.std.450) 46(FMix) 442 443 447 + 448: 46(fvec3) Select 447 443 442 449: 46(fvec3) Load 48(dvec3v) 450: 46(fvec3) FAdd 449 448 Store 48(dvec3v) 450 451: 51(fvec4) Load 53(dvec4v) 452: 51(fvec4) Load 53(dvec4v) 456: 453(bvec4) Load 455(bvec4v) - 457: 51(fvec4) ExtInst 1(GLSL.std.450) 46(FMix) 451 452 456 + 457: 51(fvec4) Select 456 452 451 458: 51(fvec4) Load 53(dvec4v) 459: 51(fvec4) FAdd 458 457 Store 53(dvec4v) 459 diff --git a/Test/baseResults/spv.Operations.frag.out b/Test/baseResults/spv.Operations.frag.out index b3a6d283..a5863729 100755 --- a/Test/baseResults/spv.Operations.frag.out +++ b/Test/baseResults/spv.Operations.frag.out @@ -281,7 +281,7 @@ Linked fragment stage: 176: 7(fvec4) Load 9(v) 177: 7(fvec4) Load 9(v) 182: 179(bvec4) Load 181(ub41) - 183: 7(fvec4) ExtInst 1(GLSL.std.450) 46(FMix) 176 177 182 + 183: 7(fvec4) Select 182 177 176 184: 7(fvec4) Load 9(v) 185: 7(fvec4) FAdd 184 183 Store 9(v) 185 From ed5fd5d8468b23ed6d0d12f09b14246e32feb68f Mon Sep 17 00:00:00 2001 From: David Neto Date: Sun, 28 Feb 2016 23:32:44 -0500 Subject: [PATCH 06/16] Support compilation via MinGW Change-Id: Ie52f0b1b2b20948c6f4b3cb5474537d36a5a3385 --- glslang/MachineIndependent/PoolAlloc.cpp | 2 +- glslang/MachineIndependent/intermOut.cpp | 2 +- glslang/OSDependent/Windows/CMakeLists.txt | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/glslang/MachineIndependent/PoolAlloc.cpp b/glslang/MachineIndependent/PoolAlloc.cpp index a41cadf1..d56979b4 100644 --- a/glslang/MachineIndependent/PoolAlloc.cpp +++ b/glslang/MachineIndependent/PoolAlloc.cpp @@ -32,8 +32,8 @@ //POSSIBILITY OF SUCH DAMAGE. // -#include "../Include/PoolAlloc.h" #include "../Include/Common.h" +#include "../Include/PoolAlloc.h" #include "../Include/InitializeGlobals.h" #include "../OSDependent/osinclude.h" diff --git a/glslang/MachineIndependent/intermOut.cpp b/glslang/MachineIndependent/intermOut.cpp index b52551ff..96b5c257 100644 --- a/glslang/MachineIndependent/intermOut.cpp +++ b/glslang/MachineIndependent/intermOut.cpp @@ -48,7 +48,7 @@ namespace { bool is_positive_infinity(double x) { #ifdef _MSC_VER return _fpclass(x) == _FPCLASS_PINF; -#elif defined __ANDROID__ || defined __linux__ +#elif defined __ANDROID__ || defined __linux__ || __MINGW32__ || __MINGW64__ return std::isinf(x) && (x >= 0); #else return isinf(x) && (x >= 0); diff --git a/glslang/OSDependent/Windows/CMakeLists.txt b/glslang/OSDependent/Windows/CMakeLists.txt index ff0b99e9..9bc73a37 100644 --- a/glslang/OSDependent/Windows/CMakeLists.txt +++ b/glslang/OSDependent/Windows/CMakeLists.txt @@ -4,6 +4,12 @@ set(SOURCES ossource.cpp ../osinclude.h) add_library(OSDependent STATIC ${SOURCES}) +# MinGW GCC complains about function pointer casts to void*. +# Turn that off with -fpermissive. +if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") + target_compile_options(OSDependent PRIVATE -fpermissive) +endif() + if(WIN32) source_group("Source" FILES ${SOURCES}) endif(WIN32) From 644c21025dbf3c0ebb52137b62b62e5d671cd82f Mon Sep 17 00:00:00 2001 From: Rex Xu Date: Fri, 18 Mar 2016 16:26:23 +0800 Subject: [PATCH 07/16] Parser: Type promotion for operator modulus(%) is missing. --- Test/baseResults/spv.400.frag.out | 20 ++++++++++++++++---- Test/spv.400.frag | 3 +++ glslang/MachineIndependent/Intermediate.cpp | 1 + 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Test/baseResults/spv.400.frag.out b/Test/baseResults/spv.400.frag.out index 0cf2c36e..651d67de 100644 --- a/Test/baseResults/spv.400.frag.out +++ b/Test/baseResults/spv.400.frag.out @@ -7,7 +7,7 @@ Linked fragment stage: // Module Version 10000 // Generated by (magic number): 80001 -// Id's are bound by 1104 +// Id's are bound by 1112 Capability Shader Capability Float64 @@ -15,7 +15,7 @@ Linked fragment stage: Capability SampledRect 1: ExtInstImport "GLSL.std.450" MemoryModel Logical GLSL450 - EntryPoint Fragment 4 "main" 13 1025 1031 1036 1048 1074 1095 1097 + EntryPoint Fragment 4 "main" 13 1025 1031 1036 1048 1074 1095 1097 1103 1105 ExecutionMode 4 OriginUpperLeft Source GLSL 400 SourceExtension "GL_ARB_separate_shader_objects" @@ -52,6 +52,8 @@ Linked fragment stage: Name 1078 "isamp2DA" Name 1095 "gl_FragCoord" Name 1097 "vl2" + Name 1103 "uo" + Name 1105 "u" Decorate 17(u2drs) DescriptorSet 0 Decorate 1023(arrayedSampler) DescriptorSet 0 Decorate 1025(i) Flat @@ -60,6 +62,7 @@ Linked fragment stage: Decorate 1078(isamp2DA) DescriptorSet 0 Decorate 1095(gl_FragCoord) BuiltIn FragCoord Decorate 1097(vl2) Location 6 + Decorate 1105(u) Flat 2: TypeVoid 3: TypeFunction 2 10: TypeFloat 32 @@ -177,6 +180,10 @@ Linked fragment stage: 1094: TypePointer Input 11(fvec4) 1095(gl_FragCoord): 1094(ptr) Variable Input 1097(vl2): 1094(ptr) Variable Input + 1102: TypePointer Output 30(int) + 1103(uo): 1102(ptr) Variable Output + 1104: TypePointer Input 30(int) + 1105(u): 1104(ptr) Variable Input 4(main): 2 Function None 3 5: Label 1017(v): 1016(ptr) Variable Function @@ -227,8 +234,13 @@ Linked fragment stage: 1100: 11(fvec4) Load 13(outp) 1101: 11(fvec4) FAdd 1100 1099 Store 13(outp) 1101 - 1102: 2 FunctionCall 6(foo23() - 1103: 2 FunctionCall 8(doubles() + 1106: 30(int) Load 1105(u) + 1107: 23(int) Load 1025(i) + 1108: 30(int) Bitcast 1107 + 1109: 30(int) UMod 1106 1108 + Store 1103(uo) 1109 + 1110: 2 FunctionCall 6(foo23() + 1111: 2 FunctionCall 8(doubles() Return FunctionEnd 6(foo23(): 2 Function None 3 diff --git a/Test/spv.400.frag b/Test/spv.400.frag index 2014b5b0..05b4370b 100644 --- a/Test/spv.400.frag +++ b/Test/spv.400.frag @@ -2,6 +2,8 @@ in vec2 c2D; flat in int i; +flat in uint u; +out uint uo; out vec4 outp; out ivec4 ioutp; out uvec4 uoutp; @@ -254,6 +256,7 @@ void main() ioutp += textureGatherOffset(isamp2DA, vec3(0.1), ivec2(i)); outp += gl_FragCoord + vl2; + uo = u % i; foo23(); doubles(); } diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp index bafcb917..e4d21e69 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -474,6 +474,7 @@ TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TInt case EOpSub: case EOpMul: case EOpDiv: + case EOpMod: case EOpVectorTimesScalar: case EOpVectorTimesMatrix: From 952543e757fb9770ed54eff72423e86b4becd52a Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Sat, 19 Mar 2016 18:10:22 -0600 Subject: [PATCH 08/16] Front-end infrastructure: simplify and localize creating symbol nodes, reducing replication. --- glslang/MachineIndependent/Intermediate.cpp | 27 ++++++++++++------- glslang/MachineIndependent/ParseHelper.cpp | 2 +- .../MachineIndependent/localintermediate.h | 7 ++--- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp index e4d21e69..66c3680c 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -61,27 +61,35 @@ namespace glslang { // Returns the added node. // -TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TSourceLoc& loc) +TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TConstUnionArray& constArray, const TSourceLoc& loc) { TIntermSymbol* node = new TIntermSymbol(id, name, type); node->setLoc(loc); - - return node; -} - -TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TConstUnionArray& constArray, const TSourceLoc& loc) -{ - TIntermSymbol* node = addSymbol(id, name, type, loc); node->setConstArray(constArray); return node; } +TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable) +{ + glslang::TSourceLoc loc; // just a null location + loc.init(); + + return addSymbol(variable, loc); +} + TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable, const TSourceLoc& loc) { return addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), variable.getConstArray(), loc); } +TIntermSymbol* TIntermediate::addSymbol(const TType& type, const TSourceLoc& loc) +{ + TConstUnionArray unionArray; // just a null constant + + return addSymbol(0, "", type, unionArray, loc); +} + // // Connect two nodes with a new parent that does a binary operation on the nodes. // @@ -1018,8 +1026,7 @@ void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymb const TAnonMember* anon = symbol.getAsAnonMember(); variable = &anon->getAnonContainer(); } - TIntermSymbol* node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variable->getType()); - node->setConstArray(variable->getConstArray()); + TIntermSymbol* node = addSymbol(*variable); linkage = growAggregate(linkage, node); } diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 8cee3e4b..28af0d40 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -1017,7 +1017,7 @@ TIntermAggregate* TParseContext::handleFunctionDefinition(const TSourceLoc& loc, loc); } } else - paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(0, "", *param.type, loc), loc); + paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc); } intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc); loopNestingLevel = 0; diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index 99267475..f25c0f11 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -162,10 +162,10 @@ public: int getNumErrors() const { return numErrors; } void addPushConstantCount() { ++numPushConstants; } bool isRecursive() const { return recursive; } - - TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TConstUnionArray&, const TSourceLoc&); - TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TSourceLoc&); + + TIntermSymbol* addSymbol(const TVariable&); TIntermSymbol* addSymbol(const TVariable&, const TSourceLoc&); + TIntermSymbol* addSymbol(const TType&, const TSourceLoc&); TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*) const; TIntermTyped* addBinaryMath(TOperator, TIntermTyped* left, TIntermTyped* right, TSourceLoc); TIntermTyped* addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc); @@ -326,6 +326,7 @@ public: static int getBaseAlignment(const TType&, int& size, int& stride, bool std140, bool rowMajor); protected: + TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TConstUnionArray&, const TSourceLoc&); void error(TInfoSink& infoSink, const char*); void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals); void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects); From 7cc0e2896ed3c2d00bc8b19f69ec88a166320343 Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Sun, 20 Mar 2016 00:46:02 -0600 Subject: [PATCH 09/16] Front-end infrastructure: Encapsulate semantic-level questions/actions about const/temp. Much about const or temp is mechanical, about actual declaration, while much is semantic, about something higher level. This commit checks every use everywhere, and for the high-level ones, substitutes an encapsulated version instead. --- SPIRV/GlslangToSpv.cpp | 4 +- Test/baseResults/310.comp.out | 4 +- glslang/Include/Types.h | 13 ++++++ glslang/MachineIndependent/Intermediate.cpp | 2 +- glslang/MachineIndependent/ParseHelper.cpp | 47 ++++++++++++--------- 5 files changed, 45 insertions(+), 25 deletions(-) diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 9b03b9a7..b837a25d 100755 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -1629,7 +1629,7 @@ spv::Id TGlslangToSpvTraverser::createSpvVariable(const glslang::TIntermSymbol* // First, steer off constants, which are not SPIR-V variables, but // can still have a mapping to a SPIR-V Id. // This includes specialization constants. - if (node->getQualifier().storage == glslang::EvqConst) { + if (node->getQualifier().isConstant()) { return createSpvSpecConstant(*node); } @@ -3732,7 +3732,7 @@ void TGlslangToSpvTraverser::addMemberDecoration(spv::Id id, int member, spv::De // - when running into a non-spec-constant, switch to createSpvConstant() spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermTyped& node) { - assert(node.getQualifier().storage == glslang::EvqConst); + assert(node.getQualifier().isConstant()); if (! node.getQualifier().specConstant) { // hand off to the non-spec-constant path diff --git a/Test/baseResults/310.comp.out b/Test/baseResults/310.comp.out index ff438e3a..d6b03322 100644 --- a/Test/baseResults/310.comp.out +++ b/Test/baseResults/310.comp.out @@ -337,7 +337,7 @@ ERROR: node is still EOpNull! 0:201 1 (const int) 0:201 Constant: 0:201 2 (const int) -0:202 Test condition and select (layout(column_major shared ) temp highp float) +0:202 Test condition and select (temp highp float) 0:202 Condition 0:202 'b' (temp bool) 0:202 true case @@ -764,7 +764,7 @@ ERROR: node is still EOpNull! 0:201 1 (const int) 0:201 Constant: 0:201 2 (const int) -0:202 Test condition and select (layout(column_major shared ) temp highp float) +0:202 Test condition and select (temp highp float) 0:202 Condition 0:202 'b' (temp bool) 0:202 true case diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h index c88bca67..1d126569 100644 --- a/glslang/Include/Types.h +++ b/glslang/Include/Types.h @@ -411,6 +411,19 @@ public: clearLayout(); } + // Drop just the storage qualification, which perhaps should + // never be done, as it is fundamentally inconsistent, but need to + // explore what downstream consumers need. + // E.g., in a deference, it is an inconsistency between: + // A) partially dereferenced resource is still in the storage class it started in + // B) partially dereferenced resource is a new temporary object + // If A, then nothing should change, if B, then everything should change, but this is half way. + void makePartialTemporary() + { + storage = EvqTemporary; + specConstant = false; + } + TStorageQualifier storage : 6; TBuiltInVariable builtIn : 8; TPrecisionQualifier precision : 3; diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp index 66c3680c..5f474911 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -822,7 +822,7 @@ TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* true // Make a selection node. // TIntermSelection* node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType()); - node->getQualifier().storage = EvqTemporary; + node->getQualifier().makeTemporary(); node->setLoc(loc); node->getQualifier().precision = std::max(trueBlock->getQualifier().precision, falseBlock->getQualifier().precision); diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 28af0d40..9057b345 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -484,7 +484,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn TIntermTyped* result = nullptr; int indexValue = 0; - if (index->getQualifier().storage == EvqConst) { + if (index->getQualifier().isConstant()) { indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst(); checkIndex(loc, base->getType(), indexValue); } @@ -495,7 +495,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn error(loc, " left of '[' is not of type array, matrix, or vector ", base->getAsSymbolNode()->getName().c_str(), ""); else error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", ""); - } else if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst) + } else if (base->getType().getQualifier().isFrontEndConstant() && index->getQualifier().isFrontEndConstant()) return intermediate.foldDereference(base, indexValue, loc); else { // at least one of base and index is variable... @@ -503,7 +503,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn if (base->getAsSymbolNode() && isIoResizeArray(base->getType())) handleIoResizeArrayAccess(loc, base); - if (index->getQualifier().storage == EvqConst) { + if (index->getQualifier().isConstant()) { if (base->getType().isImplicitlySizedArray()) updateImplicitArraySize(loc, base, indexValue); result = intermediate.addIndex(EOpIndexDirect, base, index, loc); @@ -541,10 +541,10 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn } else { // Insert valid dereferenced result TType newType(base->getType(), 0); // dereferenced type - if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst) + if (base->getType().getQualifier().isFrontEndConstant() && index->getQualifier().isFrontEndConstant()) newType.getQualifier().storage = EvqConst; else - newType.getQualifier().storage = EvqTemporary; + newType.getQualifier().makePartialTemporary(); result->setType(newType); if (anyIndexLimits) @@ -587,7 +587,7 @@ void TParseContext::handleIndexLimits(const TSourceLoc& /*loc*/, TIntermTyped* b (! limits.generalVariableIndexing && ! base->getType().getQualifier().isUniformOrBuffer() && ! base->getType().getQualifier().isPipeInput() && ! base->getType().getQualifier().isPipeOutput() && - base->getType().getQualifier().storage != EvqConst) || + ! base->getType().getQualifier().isConstant()) || (! limits.generalVaryingIndexing && (base->getType().getQualifier().isPipeInput() || base->getType().getQualifier().isPipeOutput()))) { // it's too early to know what the inductive variables are, save it for post processing @@ -849,7 +849,7 @@ TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TInterm } } if (fieldFound) { - if (base->getType().getQualifier().storage == EvqConst) + if (base->getType().getQualifier().isFrontEndConstant()) result = intermediate.foldDereference(base, member, loc); else { blockMemberExtensionCheck(loc, base, field); @@ -2039,7 +2039,7 @@ void TParseContext::rValueErrorCheck(const TSourceLoc& loc, const char* op, TInt // void TParseContext::constantValueCheck(TIntermTyped* node, const char* token) { - if (node->getQualifier().storage != EvqConst) + if (! node->getQualifier().isConstant()) error(node->getLoc(), "constant expression required", token, ""); } @@ -2204,6 +2204,7 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T int size = 0; bool constType = true; + bool specConstType = true; bool full = false; bool overFull = false; bool matrixInMatrix = false; @@ -2232,12 +2233,17 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents()) full = true; - if (function[arg].type->getQualifier().storage != EvqConst) + if (! function[arg].type->getQualifier().isConstant()) constType = false; + if (! function[arg].type->getQualifier().isSpecConstant()) + specConstType = false; } - if (constType) + if (constType) { type.getQualifier().storage = EvqConst; + if (specConstType) + type.getQualifier().specConstant = true; + } if (type.isArray()) { if (function.getParamCount() == 0) { @@ -3147,7 +3153,7 @@ void TParseContext::nonInitConstCheck(const TSourceLoc& loc, TString& identifier // if (type.getQualifier().storage == EvqConst || type.getQualifier().storage == EvqConstReadOnly) { - type.getQualifier().storage = EvqTemporary; + type.getQualifier().makeTemporary(); error(loc, "variables with qualifier 'const' must be initialized", identifier.c_str(), ""); } } @@ -4848,7 +4854,7 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp if (! initializer) { // error recovery; don't leave const without constant values if (qualifier == EvqConst) - variable->getWritableType().getQualifier().storage = EvqTemporary; + variable->getWritableType().getQualifier().makeTemporary(); return nullptr; } @@ -4868,21 +4874,22 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp } } - // Uniform and global consts require a constant initializer - if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) { + // Uniforms require a compile-time constant initializer + if (qualifier == EvqUniform && ! initializer->getType().getQualifier().isFrontEndConstant()) { error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str()); - variable->getWritableType().getQualifier().storage = EvqTemporary; + variable->getWritableType().getQualifier().makeTemporary(); return nullptr; } - if (qualifier == EvqConst && symbolTable.atGlobalLevel() && initializer->getType().getQualifier().storage != EvqConst) { + // Global consts require a constant initializer (specialization constant is okay) + if (qualifier == EvqConst && symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) { error(loc, "global const initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str()); - variable->getWritableType().getQualifier().storage = EvqTemporary; + variable->getWritableType().getQualifier().makeTemporary(); return nullptr; } // Const variables require a constant initializer, depending on version if (qualifier == EvqConst) { - if (initializer->getType().getQualifier().storage != EvqConst) { + if (! initializer->getType().getQualifier().isConstant()) { const char* initFeature = "non-constant initializer"; requireProfile(loc, ~EEsProfile, initFeature); profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); @@ -4894,7 +4901,7 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp // // "In declarations of global variables with no storage qualifier or with a const // qualifier any initializer must be a constant expression." - if (symbolTable.atGlobalLevel() && initializer->getType().getQualifier().storage != EvqConst) { + if (symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) { const char* initFeature = "non-constant global initializer"; if (relaxedErrors()) warn(loc, "not allowed in this version", initFeature, ""); @@ -4910,7 +4917,7 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp if (! initializer || ! initializer->getAsConstantUnion() || variable->getType() != initializer->getType()) { error(loc, "non-matching or non-convertible constant type for const initializer", variable->getType().getStorageQualifierString(), ""); - variable->getWritableType().getQualifier().storage = EvqTemporary; + variable->getWritableType().getQualifier().makeTemporary(); return nullptr; } From a5845766e0d89aa0b7aa597fe74a93296f4aeb0d Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Sun, 20 Mar 2016 16:46:00 -0600 Subject: [PATCH 10/16] Front-end: Add specialization-constant subtrees for const variables/symbols. --- SPIRV/GlslangToSpv.cpp | 1 + glslang/Include/intermediate.h | 20 ++++++++++------- glslang/MachineIndependent/Intermediate.cpp | 8 ++++--- glslang/MachineIndependent/ParseHelper.cpp | 17 ++++++++++++-- glslang/MachineIndependent/SymbolTable.cpp | 9 +++++--- glslang/MachineIndependent/SymbolTable.h | 22 ++++++++++++++----- glslang/MachineIndependent/intermOut.cpp | 5 +++++ .../MachineIndependent/localintermediate.h | 2 +- 8 files changed, 61 insertions(+), 23 deletions(-) diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index b837a25d..6f5697e3 100755 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -3763,6 +3763,7 @@ spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermType return builder.makeCompositeConstant(builder.makeVectorType(builder.makeUintType(32), 3), dimConstId, true); } else { spv::MissingFunctionality("specialization-constant expression trees"); + exit(1); return spv::NoResult; } } diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h index ce5fb775..ab177011 100644 --- a/glslang/Include/intermediate.h +++ b/glslang/Include/intermediate.h @@ -612,25 +612,29 @@ public: // if symbol is initialized as symbol(sym), the memory comes from the pool allocator of sym. If sym comes from // per process threadPoolAllocator, then it causes increased memory usage per compile // it is essential to use "symbol = sym" to assign to symbol - TIntermSymbol(int i, const TString& n, const TType& t) : - TIntermTyped(t), id(i) { name = n;} + TIntermSymbol(int i, const TString& n, const TType& t) + : TIntermTyped(t), id(i), constSubtree(nullptr) + { name = n; } virtual int getId() const { return id; } virtual const TString& getName() const { return name; } virtual void traverse(TIntermTraverser*); virtual TIntermSymbol* getAsSymbolNode() { return this; } virtual const TIntermSymbol* getAsSymbolNode() const { return this; } - void setConstArray(const TConstUnionArray& c) { unionArray = c; } - const TConstUnionArray& getConstArray() const { return unionArray; } + void setConstArray(const TConstUnionArray& c) { constArray = c; } + const TConstUnionArray& getConstArray() const { return constArray; } + void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; } + TIntermTyped* getConstSubtree() const { return constSubtree; } protected: int id; // the unique id of the symbol this node represents TString name; // the name of the symbol this node represents - TConstUnionArray unionArray; // if the symbol is a front-end compile-time constant, this is its value + TConstUnionArray constArray; // if the symbol is a front-end compile-time constant, this is its value + TIntermTyped* constSubtree; }; class TIntermConstantUnion : public TIntermTyped { public: - TIntermConstantUnion(const TConstUnionArray& ua, const TType& t) : TIntermTyped(t), unionArray(ua), literal(false) { } - const TConstUnionArray& getConstArray() const { return unionArray; } + TIntermConstantUnion(const TConstUnionArray& ua, const TType& t) : TIntermTyped(t), constArray(ua), literal(false) { } + const TConstUnionArray& getConstArray() const { return constArray; } virtual TIntermConstantUnion* getAsConstantUnion() { return this; } virtual const TIntermConstantUnion* getAsConstantUnion() const { return this; } virtual void traverse(TIntermTraverser*); @@ -640,7 +644,7 @@ public: void setExpression() { literal = false; } bool isLiteral() const { return literal; } protected: - const TConstUnionArray unionArray; + const TConstUnionArray constArray; bool literal; // true if node represents a literal in the source code }; diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp index 5f474911..bb4e14bc 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -61,11 +61,13 @@ namespace glslang { // Returns the added node. // -TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TConstUnionArray& constArray, const TSourceLoc& loc) +TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TConstUnionArray& constArray, + TIntermTyped* constSubtree, const TSourceLoc& loc) { TIntermSymbol* node = new TIntermSymbol(id, name, type); node->setLoc(loc); node->setConstArray(constArray); + node->setConstSubtree(constSubtree); return node; } @@ -80,14 +82,14 @@ TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable) TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable, const TSourceLoc& loc) { - return addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), variable.getConstArray(), loc); + return addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), variable.getConstArray(), variable.getConstSubtree(), loc); } TIntermSymbol* TIntermediate::addSymbol(const TType& type, const TSourceLoc& loc) { TConstUnionArray unionArray; // just a null constant - return addSymbol(0, "", type, unionArray, loc); + return addSymbol(0, "", type, unionArray, nullptr, loc); } // diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 9057b345..cd745a9d 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -4914,14 +4914,27 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp // Compile-time tagging of the variable with its constant value... initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer); - if (! initializer || ! initializer->getAsConstantUnion() || variable->getType() != initializer->getType()) { + if (! initializer || ! initializer->getType().getQualifier().isConstant() || variable->getType() != initializer->getType()) { error(loc, "non-matching or non-convertible constant type for const initializer", variable->getType().getStorageQualifierString(), ""); variable->getWritableType().getQualifier().makeTemporary(); return nullptr; } - variable->setConstArray(initializer->getAsConstantUnion()->getConstArray()); + // We either have a folded constant in getAsConstantUnion, or we have to use + // the initializer's subtree in the AST to represent the computation of a + // specialization constant. + assert(initializer->getAsConstantUnion() || initializer->getType().getQualifier().isSpecConstant()); + if (initializer->getAsConstantUnion()) + variable->setConstArray(initializer->getAsConstantUnion()->getConstArray()); + else { + // It's a specialization constant. + variable->getWritableType().getQualifier().makeSpecConstant(); + + // Keep the subtree that computes the specialization constant with the variable. + // Later, a symbol node will adopt the subtree from the variable. + variable->setConstSubtree(initializer); + } } else { // normal assigning of a value to a variable... specializationCheck(loc, initializer->getType(), "initializer"); diff --git a/glslang/MachineIndependent/SymbolTable.cpp b/glslang/MachineIndependent/SymbolTable.cpp index 9794a5d2..75f50402 100644 --- a/glslang/MachineIndependent/SymbolTable.cpp +++ b/glslang/MachineIndependent/SymbolTable.cpp @@ -252,11 +252,14 @@ TVariable::TVariable(const TVariable& copyOf) : TSymbol(copyOf) if (copyOf.numExtensions != 0) setExtensions(copyOf.numExtensions, copyOf.extensions); - if (! copyOf.unionArray.empty()) { + if (! copyOf.constArray.empty()) { assert(! copyOf.type.isStruct()); - TConstUnionArray newArray(copyOf.unionArray, 0, copyOf.unionArray.size()); - unionArray = newArray; + TConstUnionArray newArray(copyOf.constArray, 0, copyOf.constArray.size()); + constArray = newArray; } + + // don't support specialization-constant subtrees in cloned tables + constSubtree = nullptr; } TVariable* TVariable::clone() const diff --git a/glslang/MachineIndependent/SymbolTable.h b/glslang/MachineIndependent/SymbolTable.h index ad73b3b5..9877ab7f 100644 --- a/glslang/MachineIndependent/SymbolTable.h +++ b/glslang/MachineIndependent/SymbolTable.h @@ -135,7 +135,7 @@ protected: // // Variable class, meaning a symbol that's not a function. // -// There could be a separate class heirarchy for Constant variables; +// There could be a separate class hierarchy for Constant variables; // Only one of int, bool, or float, (or none) is correct for // any particular use, but it's easy to do this way, and doesn't // seem worth having separate classes, and "getConst" can't simply return @@ -144,7 +144,10 @@ protected: // class TVariable : public TSymbol { public: - TVariable(const TString *name, const TType& t, bool uT = false ) : TSymbol(name), userType(uT) { type.shallowCopy(t); } + TVariable(const TString *name, const TType& t, bool uT = false ) + : TSymbol(name), + userType(uT), + constSubtree(nullptr) { type.shallowCopy(t); } virtual TVariable* clone() const; virtual ~TVariable() { } @@ -153,9 +156,11 @@ public: virtual const TType& getType() const { return type; } virtual TType& getWritableType() { assert(writable); return type; } virtual bool isUserType() const { return userType; } - virtual const TConstUnionArray& getConstArray() const { return unionArray; } - virtual TConstUnionArray& getWritableConstArray() { assert(writable); return unionArray; } - virtual void setConstArray(const TConstUnionArray& constArray) { unionArray = constArray; } + virtual const TConstUnionArray& getConstArray() const { return constArray; } + virtual TConstUnionArray& getWritableConstArray() { assert(writable); return constArray; } + virtual void setConstArray(const TConstUnionArray& array) { constArray = array; } + virtual void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; } + virtual TIntermTyped* getConstSubtree() const { return constSubtree; } virtual void dump(TInfoSink &infoSink) const; @@ -167,7 +172,12 @@ protected: bool userType; // we are assuming that Pool Allocator will free the memory allocated to unionArray // when this object is destroyed - TConstUnionArray unionArray; + + // TODO: these two should be a union + // A variable could be a compile-time constant, or a specialization + // constant, or neither, but never both. + TConstUnionArray constArray; // for compile-time constant value + TIntermTyped* constSubtree; // for specialization constant computation }; // diff --git a/glslang/MachineIndependent/intermOut.cpp b/glslang/MachineIndependent/intermOut.cpp index 96b5c257..eb6c30cd 100644 --- a/glslang/MachineIndependent/intermOut.cpp +++ b/glslang/MachineIndependent/intermOut.cpp @@ -605,6 +605,11 @@ void TOutputTraverser::visitSymbol(TIntermSymbol* node) if (! node->getConstArray().empty()) OutputConstantUnion(infoSink, node, node->getConstArray(), depth + 1); + else if (node->getConstSubtree()) { + incrementDepth(node); + node->getConstSubtree()->traverse(this); + decrementDepth(); + } } bool TOutputTraverser::visitLoop(TVisit /* visit */, TIntermLoop* node) diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index f25c0f11..5e5a2e77 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -326,7 +326,7 @@ public: static int getBaseAlignment(const TType&, int& size, int& stride, bool std140, bool rowMajor); protected: - TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TConstUnionArray&, const TSourceLoc&); + TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TConstUnionArray&, TIntermTyped* subtree, const TSourceLoc&); void error(TInfoSink& infoSink, const char*); void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals); void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects); From 6d2b07dc3952739b7d540cc326a21eff8c3ef935 Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Sun, 20 Mar 2016 18:45:23 -0600 Subject: [PATCH 11/16] Front-end: propagate specialization-constness through conversions and swizzles. --- glslang/Include/Types.h | 1 + glslang/MachineIndependent/Intermediate.cpp | 10 ++++++++-- glslang/MachineIndependent/ParseHelper.cpp | 6 ++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h index 1d126569..05681c9a 100644 --- a/glslang/Include/Types.h +++ b/glslang/Include/Types.h @@ -740,6 +740,7 @@ public: } void makeSpecConstant() { + storage = EvqConst; specConstant = true; } static const char* getLayoutPackingString(TLayoutPacking packing) diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp index bb4e14bc..777de2d6 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -262,6 +262,7 @@ TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermTyped* child, TSo // // For constructors, we are now done, it was all in the conversion. + // TODO: but, did this bypass constant folding? // switch (op) { case EOpConstructInt: @@ -291,7 +292,7 @@ TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermTyped* child, TSo if (child->getAsConstantUnion()) return child->getAsConstantUnion()->fold(op, node->getType()); - // If it's a specialiation constant, the result is too. + // If it's a specialization constant, the result is too. if (child->getType().getQualifier().isSpecConstant()) node->getWritableType().getQualifier().makeSpecConstant(); @@ -616,6 +617,12 @@ TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TInt newNode->setLoc(node->getLoc()); newNode->setOperand(node); + // TODO: it seems that some unary folding operations should occur here, but are not + + // Propagate specialization-constant-ness. + if (node->getType().getQualifier().isSpecConstant()) + newNode->getWritableType().getQualifier().makeSpecConstant(); + return newNode; } @@ -883,7 +890,6 @@ TIntermConstantUnion* TIntermediate::addConstantUnion(double d, TBasicType baseT TIntermTyped* TIntermediate::addSwizzle(TVectorFields& fields, const TSourceLoc& loc) { - TIntermAggregate* node = new TIntermAggregate(EOpSequence); node->setLoc(loc); diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index cd745a9d..d6f41151 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -820,6 +820,9 @@ TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TInterm return result; else { TType type(base->getBasicType(), EvqTemporary, fields.num); + // Swizzle operations propagate specialization-constantness + if (base->getQualifier().isSpecConstant()) + type.getQualifier().makeSpecConstant(); return addConstructor(loc, base, type, mapTypeToConstructorOp(type)); } } @@ -837,6 +840,9 @@ TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TInterm result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc); result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, (int) vectorString.size())); } + // Swizzle operations propagate specialization-constantness + if (base->getType().getQualifier().isSpecConstant()) + result->getWritableType().getQualifier().makeSpecConstant(); } } else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) { const TTypeList* fields = base->getType().getStruct(); From a132af5b78914e4dadcd66a9e8837de10b3a3c9f Mon Sep 17 00:00:00 2001 From: Andrew Woloszyn Date: Mon, 7 Mar 2016 13:23:09 -0500 Subject: [PATCH 12/16] Updated the includer interface to allow relative includes. This plumbs both the current file path and the include depth back up to the includer. This allows the includer to properly support relative paths. This also replaces the string copy that was done during include with a zero-copy method of accomplishing the same thing. This prevents extra copies of entire files. --- StandAlone/StandAlone.cpp | 3 +- glslang/MachineIndependent/Scan.h | 53 +++++++++-- glslang/MachineIndependent/ShaderLang.cpp | 19 ++-- .../MachineIndependent/preprocessor/Pp.cpp | 26 +++--- .../preprocessor/PpContext.cpp | 6 +- .../preprocessor/PpContext.h | 87 +++++++++++++++---- glslang/Public/ShaderLang.h | 80 +++++++++++++++-- 7 files changed, 220 insertions(+), 54 deletions(-) diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index cc59d389..6ebdb7f5 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -699,8 +699,9 @@ void CompileAndLinkShaderUnits(std::vector compUnits) if (Options & EOptionOutputPreprocessed) { std::string str; + glslang::TShader::ForbidInclude includer; if (shader->preprocess(&Resources, defaultVersion, ENoProfile, false, false, - messages, &str, glslang::TShader::ForbidInclude())) { + messages, &str, includer)) { PutsIfNonEmpty(str.c_str()); } else { CompileFailed = true; diff --git a/glslang/MachineIndependent/Scan.h b/glslang/MachineIndependent/Scan.h index 936b99f1..f3102fbf 100644 --- a/glslang/MachineIndependent/Scan.h +++ b/glslang/MachineIndependent/Scan.h @@ -51,10 +51,10 @@ const int EndOfInput = -1; // class TInputScanner { public: - TInputScanner(int n, const char* const s[], size_t L[], const char* const* names = nullptr, int b = 0, int f = 0) : + TInputScanner(int n, const char* const s[], size_t L[], const char* const* names = nullptr, int b = 0, int f = 0, bool single = false) : numSources(n), sources(reinterpret_cast(s)), // up to this point, common usage is "char*", but now we need positive 8-bit characters - lengths(L), currentSource(0), currentChar(0), stringBias(b), finale(f) + lengths(L), currentSource(0), currentChar(0), stringBias(b), finale(f), singleLogical(single) { loc = new TSourceLoc[numSources]; for (int i = 0; i < numSources; ++i) { @@ -67,6 +67,10 @@ public: loc[currentSource].string = -stringBias; loc[currentSource].line = 1; loc[currentSource].column = 0; + logicalSourceLoc.string = 0; + logicalSourceLoc.line = 1; + logicalSourceLoc.column = 0; + logicalSourceLoc.name = loc[0].name; } virtual ~TInputScanner() @@ -82,8 +86,11 @@ public: int ret = peek(); ++loc[currentSource].column; + ++logicalSourceLoc.column; if (ret == '\n') { ++loc[currentSource].line; + ++logicalSourceLoc.line; + logicalSourceLoc.column = 0; loc[currentSource].column = 0; } advance(); @@ -118,6 +125,7 @@ public: if (currentChar > 0) { --currentChar; --loc[currentSource].column; + --logicalSourceLoc.column; if (loc[currentSource].column < 0) { // We've moved back past a new line. Find the // previous newline (or start of the file) to compute @@ -129,6 +137,7 @@ public: } --chIndex; } + logicalSourceLoc.column = (int)(currentChar - chIndex); loc[currentSource].column = (int)(currentChar - chIndex); } } else { @@ -141,23 +150,49 @@ public: } else currentChar = lengths[currentSource] - 1; } - if (peek() == '\n') + if (peek() == '\n') { --loc[currentSource].line; + --logicalSourceLoc.line; + } } // for #line override - void setLine(int newLine) { loc[getLastValidSourceIndex()].line = newLine; } - void setFile(const char* filename) { loc[getLastValidSourceIndex()].name = filename; } + void setLine(int newLine) + { + logicalSourceLoc.line = newLine; + loc[getLastValidSourceIndex()].line = newLine; + } + + // for #line override in filename based parsing + void setFile(const char* filename) + { + logicalSourceLoc.name = filename; + loc[getLastValidSourceIndex()].name = filename; + } + void setString(int newString) { + logicalSourceLoc.string = newString; loc[getLastValidSourceIndex()].string = newString; + logicalSourceLoc.name = nullptr; loc[getLastValidSourceIndex()].name = nullptr; } // for #include content indentation - void setColumn(int col) { loc[getLastValidSourceIndex()].column = col; } + void setColumn(int col) + { + logicalSourceLoc.column = col; + loc[getLastValidSourceIndex()].column = col; + } - const TSourceLoc& getSourceLoc() const { return loc[std::max(0, std::min(currentSource, numSources - finale - 1))]; } + const TSourceLoc& getSourceLoc() const + { + if (singleLogical) { + return logicalSourceLoc; + } else { + return loc[std::max(0, std::min(currentSource, numSources - finale - 1))]; + } + } // Returns the index (starting from 0) of the most recent valid source string we are reading from. int getLastValidSourceIndex() const { return std::min(currentSource, numSources - 1); } @@ -204,6 +239,10 @@ protected: int stringBias; // the first string that is the user's string number 0 int finale; // number of internal strings after user's last string + + TSourceLoc logicalSourceLoc; + bool singleLogical; // treats the strings as a single logical string. + // locations will be reported from the first string. }; } // end namespace glslang diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index 1f835539..2a6e1e19 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -131,7 +131,8 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil TIntermediate intermediate(language, version, profile); TParseContext parseContext(symbolTable, intermediate, true, version, profile, spv, vulkan, language, infoSink); - TPpContext ppContext(parseContext, TShader::ForbidInclude()); + TShader::ForbidInclude includer; + TPpContext ppContext(parseContext, "", includer); TScanContext scanContext(parseContext); parseContext.setScanContext(&scanContext); parseContext.setPpContext(&ppContext); @@ -482,7 +483,7 @@ bool ProcessDeferred( TIntermediate& intermediate, // returned tree, etc. ProcessingContext& processingContext, bool requireNonempty, - const TShader::Includer& includer + TShader::Includer& includer ) { if (! InitThread()) @@ -550,7 +551,6 @@ bool ProcessDeferred( version = defaultVersion; profile = defaultProfile; } - int spv = (messages & EShMsgSpvRules) ? 100 : 0; // TODO find path to get real version number here, for now non-0 is what matters bool goodVersion = DeduceVersionProfile(compiler->infoSink, compiler->getLanguage(), versionNotFirst, defaultVersion, version, profile, spv); bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst)); @@ -590,7 +590,7 @@ bool ProcessDeferred( TParseContext parseContext(symbolTable, intermediate, false, version, profile, spv, vulkan, compiler->getLanguage(), compiler->infoSink, forwardCompatible, messages); glslang::TScanContext scanContext(parseContext); - TPpContext ppContext(parseContext, includer); + TPpContext ppContext(parseContext, names[numPre]? names[numPre]: "", includer); parseContext.setScanContext(&scanContext); parseContext.setPpContext(&ppContext); parseContext.setLimits(*resources); @@ -863,7 +863,7 @@ bool PreprocessDeferred( bool forceDefaultVersionAndProfile, bool forwardCompatible, // give errors for use of deprecated features EShMessages messages, // warnings/errors/AST; things to print out - const TShader::Includer& includer, + TShader::Includer& includer, TIntermediate& intermediate, // returned tree, etc. std::string* outputString) { @@ -902,7 +902,7 @@ bool CompileDeferred( bool forwardCompatible, // give errors for use of deprecated features EShMessages messages, // warnings/errors/AST; things to print out TIntermediate& intermediate,// returned tree, etc. - const TShader::Includer& includer) + TShader::Includer& includer) { DoFullParse parser; return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, @@ -1051,9 +1051,10 @@ int ShCompile( compiler->infoSink.debug.erase(); TIntermediate intermediate(compiler->getLanguage()); + TShader::ForbidInclude includer; bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr, "", optLevel, resources, defaultVersion, ENoProfile, false, - forwardCompatible, messages, intermediate, TShader::ForbidInclude()); + forwardCompatible, messages, intermediate, includer); // // Call the machine dependent compiler @@ -1361,7 +1362,7 @@ void TShader::setStringsWithLengthsAndNames( // Returns true for success. // bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, - bool forwardCompatible, EShMessages messages, const Includer& includer) + bool forwardCompatible, EShMessages messages, Includer& includer) { if (! InitThread()) return false; @@ -1389,7 +1390,7 @@ bool TShader::preprocess(const TBuiltInResource* builtInResources, bool forceDefaultVersionAndProfile, bool forwardCompatible, EShMessages message, std::string* output_string, - const TShader::Includer& includer) + Includer& includer) { if (! InitThread()) return false; diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp index 9a33a776..ed554359 100644 --- a/glslang/MachineIndependent/preprocessor/Pp.cpp +++ b/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -611,24 +611,28 @@ int TPpContext::CPPinclude(TPpToken* ppToken) if (token != '\n' && token != EndOfInput) { parseContext.ppError(ppToken->loc, "extra content after file designation", "#include", ""); } else { - auto include = includer.include(filename.c_str()); - std::string sourceName = include.first; - std::string replacement = include.second; - if (!sourceName.empty()) { - if (!replacement.empty()) { + TShader::Includer::IncludeResult* res = includer.include(filename.c_str(), TShader::Includer::EIncludeRelative, currentSourceFile.c_str(), includeStack.size() + 1); + if (res && !res->file_name.empty()) { + if (res->file_data && res->file_length) { const bool forNextLine = parseContext.lineDirectiveShouldSetNextLine(); - std::ostringstream content; - content << "#line " << forNextLine << " " << "\"" << sourceName << "\"\n"; - content << replacement << (replacement.back() == '\n' ? "" : "\n"); - content << "#line " << directiveLoc.line + forNextLine << " " << directiveLoc.getStringNameOrNum() << "\n"; - pushInput(new TokenizableString(directiveLoc, content.str(), this)); + std::ostringstream prologue; + std::ostringstream epilogue; + prologue << "#line " << forNextLine << " " << "\"" << res->file_name << "\"\n"; + epilogue << (res->file_data[res->file_length - 1] == '\n'? "" : "\n") << "#line " << directiveLoc.line + forNextLine << " " << directiveLoc.getStringNameOrNum() << "\n"; + pushInput(new TokenizableIncludeFile(directiveLoc, prologue.str(), res, epilogue.str(), this)); } // At EOF, there's no "current" location anymore. if (token != EndOfInput) parseContext.setCurrentColumn(0); // Don't accidentally return EndOfInput, which will end all preprocessing. return '\n'; } else { - parseContext.ppError(directiveLoc, replacement.c_str(), "#include", ""); + std::string message = + res ? std::string(res->file_data, res->file_length) + : std::string("Could not process include directive"); + parseContext.ppError(directiveLoc, message.c_str(), "#include", ""); + if (res) { + includer.releaseInclude(res); + } } } } diff --git a/glslang/MachineIndependent/preprocessor/PpContext.cpp b/glslang/MachineIndependent/preprocessor/PpContext.cpp index b8d2c737..37c2a5f2 100644 --- a/glslang/MachineIndependent/preprocessor/PpContext.cpp +++ b/glslang/MachineIndependent/preprocessor/PpContext.cpp @@ -83,8 +83,10 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace glslang { -TPpContext::TPpContext(TParseContext& pc, const TShader::Includer& inclr) : - preamble(0), strings(0), parseContext(pc), includer(inclr), inComment(false) +TPpContext::TPpContext(TParseContext& pc, const std::string& rootFileName, TShader::Includer& inclr) : + preamble(0), strings(0), parseContext(pc), includer(inclr), inComment(false), + rootFileName(rootFileName), + currentSourceFile(rootFileName) { InitAtomTable(); InitScanner(); diff --git a/glslang/MachineIndependent/preprocessor/PpContext.h b/glslang/MachineIndependent/preprocessor/PpContext.h index f1d54691..113215bd 100644 --- a/glslang/MachineIndependent/preprocessor/PpContext.h +++ b/glslang/MachineIndependent/preprocessor/PpContext.h @@ -78,6 +78,7 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef PPCONTEXT_H #define PPCONTEXT_H +#include #include #include "../ParseHelper.h" @@ -121,7 +122,7 @@ class TInputScanner; // Don't expect too much in terms of OO design. class TPpContext { public: - TPpContext(TParseContext&, const TShader::Includer&); + TPpContext(TParseContext&, const std::string& rootFileName, TShader::Includer&); virtual ~TPpContext(); void setPreamble(const char* preamble, size_t length); @@ -290,7 +291,7 @@ protected: // // Used to obtain #include content. - const TShader::Includer& includer; + TShader::Includer& includer; int InitCPP(); int CPPdefine(TPpToken * ppToken); @@ -430,16 +431,30 @@ protected: TInputScanner* input; }; - // Holds a string that can be tokenized via the tInput interface. - class TokenizableString : public tInput { + // Holds a reference to included file data, as well as a + // prologue and an epilogue string. This can be scanned using the tInput + // interface and acts as a single source string. + class TokenizableIncludeFile : public tInput { public: - // Copies str, which must be non-empty. - TokenizableString(const TSourceLoc& startLoc, const std::string& str, TPpContext* pp) + // Copies prologue and epilogue. The includedFile must remain valid + // until this TokenizableIncludeFile is no longer used. + TokenizableIncludeFile(const TSourceLoc& startLoc, + const std::string& prologue, + TShader::Includer::IncludeResult* includedFile, + const std::string& epilogue, + TPpContext* pp) : tInput(pp), - str_(str), - strings(str_.data()), - length(str_.size()), - scanner(1, &strings, &length), + prologue_(prologue), + includedFile_(includedFile), + epilogue_(epilogue), + strings({prologue_.data(), includedFile_->file_data, epilogue_.data()}), + lengths({prologue_.size(), includedFile_->file_length, epilogue_.size()}), + names({ + includedFile_->file_name.c_str(), + includedFile_->file_name.c_str(), + includedFile_->file_name.c_str() + }), + scanner(3, strings, lengths, names, 0, 0, true), prevScanner(nullptr), stringInput(pp, scanner) { scanner.setLine(startLoc.line); @@ -456,16 +471,34 @@ protected: { prevScanner = pp->parseContext.getScanner(); pp->parseContext.setScanner(&scanner); + pp->push_include(includedFile_); + } + + void notifyDeleted() override + { + pp->parseContext.setScanner(prevScanner); + pp->pop_include(); } - void notifyDeleted() override { pp->parseContext.setScanner(prevScanner); } private: - // Stores the titular string. - const std::string str_; - // Will point to str_[0] and be passed to scanner constructor. - const char* const strings; + // Stores the prologue for this string. + const std::string prologue_; + + // Stores the epilogue for this string. + const std::string epilogue_; + + // Points to the IncludeResult that this TokenizableIncludeFile represents. + TShader::Includer::IncludeResult* includedFile_; + + // Will point to prologue_, includedFile_->file_data and epilogue_ + // This is passed to scanner constructor. + // These do not own the storage and it must remain valid until this + // object has been destroyed. + const char* strings[3]; // Length of str_, passed to scanner constructor. - size_t length; + size_t lengths[3]; + // String names + const char* names[3]; // Scans over str_. TInputScanner scanner; // The previous effective scanner before the scanner in this instance @@ -480,6 +513,24 @@ protected: void missingEndifCheck(); int lFloatConst(int len, int ch, TPpToken* ppToken); + void push_include(TShader::Includer::IncludeResult* result) + { + currentSourceFile = result->file_name; + includeStack.push(result); + } + + void pop_include() + { + TShader::Includer::IncludeResult* include = includeStack.top(); + includeStack.pop(); + includer.releaseInclude(include); + if (includeStack.empty()) { + currentSourceFile = rootFileName; + } else { + currentSourceFile = includeStack.top()->file_name; + } + } + bool inComment; // @@ -487,8 +538,12 @@ protected: // typedef TUnorderedMap TAtomMap; typedef TVector TStringMap; + TAtomMap atomMap; TStringMap stringMap; + std::stack includeStack; + std::string currentSourceFile; + std::string rootFileName; int nextAtom; void InitAtomTable(); void AddAtomFixed(const char* s, int atom); diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index 702b66f4..0771d194 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -292,25 +292,89 @@ public: void setPreamble(const char* s) { preamble = s; } // Interface to #include handlers. + // + // To support #include, a client of Glslang does the following: + // 1. Call setStringsWithNames to set the source strings and associated + // names. For example, the names could be the names of the files + // containing the shader sources. + // 2. Call parse with an Includer. + // + // When the Glslang parser encounters an #include directive, it calls + // the Includer's include method with the the requested include name + // together with the current string name. The returned IncludeResult + // contains the fully resolved name of the included source, together + // with the source text that should replace the #include directive + // in the source stream. After parsing that source, Glslang will + // release the IncludeResult object. class Includer { public: - // On success, returns the full path and content of the file with the given - // filename that replaces "#include filename". On failure, returns an empty - // string and an error message. - virtual std::pair include(const char* filename) const = 0; + typedef enum { + EIncludeRelative, // For #include "something" + EIncludeStandard // Reserved. For #include + } IncludeType; + + // An IncludeResult contains the resolved name and content of a source + // inclusion. + struct IncludeResult { + // For a successful inclusion, the fully resolved name of the requested + // include. For example, in a filesystem-based includer, full resolution + // should convert a relative path name into an absolute path name. + // For a failed inclusion, this is an empty string. + std::string file_name; + // The content and byte length of the requested inclusion. The + // Includer producing this IncludeResult retains ownership of the + // storage. + // For a failed inclusion, the file_data + // field points to a string containing error details. + const char* file_data; + const size_t file_length; + // Include resolver's context. + void* user_data; + }; + + // Resolves an inclusion request by name, type, current source name, + // and include depth. + // On success, returns an IncludeResult containing the resolved name + // and content of the include. On failure, returns an IncludeResult + // with an empty string for the file_name and error details in the + // file_data field. The Includer retains ownership of the contents + // of the returned IncludeResult value, and those contents must + // remain valid until the releaseInclude method is called on that + // IncludeResult object. + virtual IncludeResult* include(const char* requested_source, + IncludeType type, + const char* requesting_source, + size_t inclusion_depth) = 0; + // Signals that the parser will no longer use the contents of the + // specified IncludeResult. + virtual void releaseInclude(IncludeResult* result) = 0; }; // Returns an error message for any #include directive. class ForbidInclude : public Includer { public: - std::pair include(const char* /*filename*/) const override + IncludeResult* include(const char*, IncludeType, const char*, size_t) override { - return std::make_pair("", "unexpected include directive"); + static const char unexpected_include[] = + "unexpected include directive"; + return new IncludeResult( + {"", unexpected_include, sizeof(unexpected_include) - 1, nullptr}); + } + virtual void releaseInclude(IncludeResult* result) override + { + delete result; } }; + bool parse(const TBuiltInResource* res, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages messages) + { + TShader::ForbidInclude includer; + return parse(res, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, forwardCompatible, messages, includer); + } + bool parse(const TBuiltInResource*, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, - bool forwardCompatible, EShMessages, const Includer& = ForbidInclude()); + bool forwardCompatible, EShMessages, Includer&); // Equivalent to parse() without a default profile and without forcing defaults. // Provided for backwards compatibility. @@ -318,7 +382,7 @@ public: bool preprocess(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, bool forwardCompatible, EShMessages message, std::string* outputString, - const TShader::Includer& includer); + Includer& includer); const char* getInfoLog(); const char* getInfoDebugLog(); From ddb65a46f7d851c2c94e3544eeff3877ae4c8fde Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Mon, 21 Mar 2016 12:55:00 -0600 Subject: [PATCH 13/16] Front-end: Propagate spec-constness up through aggregate constructors. --- glslang/MachineIndependent/ParseHelper.cpp | 25 +++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index d6f41151..13296318 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -2210,7 +2210,7 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T int size = 0; bool constType = true; - bool specConstType = true; + bool specConstType = false; // value is only valid if constType is true bool full = false; bool overFull = false; bool matrixInMatrix = false; @@ -2241,14 +2241,15 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T if (! function[arg].type->getQualifier().isConstant()) constType = false; - if (! function[arg].type->getQualifier().isSpecConstant()) - specConstType = false; + if (function[arg].type->getQualifier().isSpecConstant()) + specConstType = true; } if (constType) { - type.getQualifier().storage = EvqConst; if (specConstType) - type.getQualifier().specConstant = true; + type.getQualifier().makeSpecConstant(); + else + type.getQualifier().storage = EvqConst; } if (type.isArray()) { @@ -5101,12 +5102,16 @@ TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* // if the structure constructor contains more than one parameter, then construct // each parameter - int paramCount = 0; // keeps a track of the constructor parameter number being checked + int paramCount = 0; // keeps track of the constructor parameter number being checked // for each parameter to the constructor call, check to see if the right type is passed or convert them // to the right type if possible (and allowed). // for structure constructors, just check if the right type is passed, no conversion is allowed. + // We don't know "top down" whether type is a specialization constant, + // but a const becomes a specialization constant if any of its children are. + bool specConst = false; + for (TIntermSequence::iterator p = sequenceVector.begin(); p != sequenceVector.end(); p++, paramCount++) { if (type.isArray()) @@ -5116,13 +5121,17 @@ TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* else newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true); - if (newNode) + if (newNode) { *p = newNode; - else + if (newNode->getType().getQualifier().isSpecConstant()) + specConst = true; + } else return nullptr; } TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc); + if (constructor->getType().getQualifier().isConstant() && specConst) + constructor->getWritableType().getQualifier().makeSpecConstant(); return constructor; } From 272afd2d0ae76b372f3896f47418623aa645c006 Mon Sep 17 00:00:00 2001 From: Andrew Woloszyn Date: Mon, 21 Mar 2016 15:56:31 -0400 Subject: [PATCH 14/16] Fixed compilation issue introduced by my last commit --- glslang/MachineIndependent/Scan.h | 8 ++++++ .../preprocessor/PpContext.h | 27 +++++++++++-------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/glslang/MachineIndependent/Scan.h b/glslang/MachineIndependent/Scan.h index f3102fbf..219049be 100644 --- a/glslang/MachineIndependent/Scan.h +++ b/glslang/MachineIndependent/Scan.h @@ -170,6 +170,14 @@ public: loc[getLastValidSourceIndex()].name = filename; } + void setFile(const char* filename, size_t i) + { + if (i == getLastValidSourceIndex()) { + logicalSourceLoc.name = filename; + } + loc[i].name = filename; + } + void setString(int newString) { logicalSourceLoc.string = newString; diff --git a/glslang/MachineIndependent/preprocessor/PpContext.h b/glslang/MachineIndependent/preprocessor/PpContext.h index 113215bd..b68c6916 100644 --- a/glslang/MachineIndependent/preprocessor/PpContext.h +++ b/glslang/MachineIndependent/preprocessor/PpContext.h @@ -447,19 +447,24 @@ protected: prologue_(prologue), includedFile_(includedFile), epilogue_(epilogue), - strings({prologue_.data(), includedFile_->file_data, epilogue_.data()}), - lengths({prologue_.size(), includedFile_->file_length, epilogue_.size()}), - names({ - includedFile_->file_name.c_str(), - includedFile_->file_name.c_str(), - includedFile_->file_name.c_str() - }), scanner(3, strings, lengths, names, 0, 0, true), prevScanner(nullptr), - stringInput(pp, scanner) { - scanner.setLine(startLoc.line); - scanner.setString(startLoc.string); - scanner.setFile(startLoc.name); + stringInput(pp, scanner) + { + strings[0] = prologue_.data(); + strings[1] = includedFile_->file_data; + strings[2] = epilogue_.data(); + + lengths[0] = prologue_.size(); + lengths[1] = includedFile_->file_length; + lengths[2] = epilogue_.size(); + + scanner.setLine(startLoc.line); + scanner.setString(startLoc.string); + + scanner.setFile(startLoc.name, 0); + scanner.setFile(startLoc.name, 1); + scanner.setFile(startLoc.name, 2); } // tInput methods: From 4e8bf59778f850b7ac9e6dc84820649f50981470 Mon Sep 17 00:00:00 2001 From: Rex Xu Date: Tue, 22 Mar 2016 15:42:07 +0800 Subject: [PATCH 15/16] Parser: Fix a build issue (VS2012). --- glslang/Public/ShaderLang.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index 0771d194..c9182618 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -357,8 +357,9 @@ public: { static const char unexpected_include[] = "unexpected include directive"; - return new IncludeResult( - {"", unexpected_include, sizeof(unexpected_include) - 1, nullptr}); + static const IncludeResult unexpectedIncludeResult = + {"", unexpected_include, sizeof(unexpected_include) - 1, nullptr}; + return new IncludeResult(unexpectedIncludeResult); } virtual void releaseInclude(IncludeResult* result) override { From 0840838d1739f2d5ebfe2037f4c6f09478710689 Mon Sep 17 00:00:00 2001 From: qining Date: Mon, 21 Mar 2016 09:51:37 -0400 Subject: [PATCH 16/16] Support specialization composite constants Fix issue #163, support creation and reference of composite type specialization constants. e.g.: ``` layout(constant_id = 200) const float myfloat = 1.25; layout(constant_id = 201) const int myint = 14; struct structtype { float f; int i; }; const structtype outer_struct_var = {myfloat, myint}; void main(){} ``` generated code (use glslangValidator): ``` // Module Version 10000 // Generated by (magic number): 80001 // Id's are bound by 12 Capability Shader 1: ExtInstImport "GLSL.std.450" MemoryModel Logical GLSL450 EntryPoint Vertex 4 "main" Source GLSL 450 Name 4 "main" Name 10 "structtype" MemberName 10(structtype) 0 "f" MemberName 10(structtype) 1 "i" Decorate 7 SpecId 200 Decorate 9 SpecId 201 2: TypeVoid 3: TypeFunction 2 6: TypeFloat 32 7: 6(float) SpecConstant 1067450368 8: TypeInt 32 1 9: 8(int) SpecConstant 14 10(structtype): TypeStruct 6(float) 8(int) 11:10(structtype) SpecConstantComposite 7 9 4(main): 2 Function None 3 5: Label Return FunctionEnd ``` Rname two function names to match their functionalities. 1) Rename `GlslangToSpvTraverser::createSpvSpecConstant()` to `createSpvConstant()`; 2) Rename `GlslangToSpvTraverser::createSpvConstant()` to `createSpvConstantFromConstUnionArray()` Add function `GlslangToSpvTraverser::createSpvConstantFromSubTree()` to handle constant creation from sub trees (e.g.: specialization constants). Related PR: #208 --- SPIRV/GlslangToSpv.cpp | 87 +++++++-- .../spv.specConstantComposite.vert.out | 172 ++++++++++++++++++ Test/spv.specConstantComposite.vert | 93 ++++++++++ Test/test-spirv-list | 1 + glslang/Include/Types.h | 1 + glslang/MachineIndependent/ParseHelper.cpp | 27 ++- 6 files changed, 362 insertions(+), 19 deletions(-) create mode 100644 Test/baseResults/spv.specConstantComposite.vert.out create mode 100644 Test/spv.specConstantComposite.vert diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 6f5697e3..dd5afd4b 100755 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -128,8 +128,9 @@ protected: void addDecoration(spv::Id id, spv::Decoration dec, unsigned value); void addMemberDecoration(spv::Id id, int member, spv::Decoration dec); void addMemberDecoration(spv::Id id, int member, spv::Decoration dec, unsigned value); - spv::Id createSpvSpecConstant(const glslang::TIntermTyped&); - spv::Id createSpvConstant(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst, bool specConstant); + spv::Id createSpvConstant(const glslang::TIntermTyped&); + spv::Id createSpvConstantFromConstUnionArray(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst, bool specConstant); + spv::Id createSpvConstantFromConstSubTree(const glslang::TIntermTyped* subTree); bool isTrivialLeaf(const glslang::TIntermTyped* node); bool isTrivial(const glslang::TIntermTyped* node); spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right); @@ -1520,7 +1521,7 @@ bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::T void TGlslangToSpvTraverser::visitConstantUnion(glslang::TIntermConstantUnion* node) { int nextConst = 0; - spv::Id constant = createSpvConstant(node->getType(), node->getConstArray(), nextConst, false); + spv::Id constant = createSpvConstantFromConstUnionArray(node->getType(), node->getConstArray(), nextConst, false); builder.clearAccessChain(); builder.setAccessChainRValue(constant); @@ -1630,7 +1631,7 @@ spv::Id TGlslangToSpvTraverser::createSpvVariable(const glslang::TIntermSymbol* // can still have a mapping to a SPIR-V Id. // This includes specialization constants. if (node->getQualifier().isConstant()) { - return createSpvSpecConstant(*node); + return createSpvConstant(*node); } // Now, handle actual variables @@ -3730,7 +3731,7 @@ void TGlslangToSpvTraverser::addMemberDecoration(spv::Id id, int member, spv::De // recursively walks. So, this function walks the "top" of the tree: // - emit specialization constant-building instructions for specConstant // - when running into a non-spec-constant, switch to createSpvConstant() -spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermTyped& node) +spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TIntermTyped& node) { assert(node.getQualifier().isConstant()); @@ -3738,7 +3739,7 @@ spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermType // hand off to the non-spec-constant path assert(node.getAsConstantUnion() != nullptr || node.getAsSymbolNode() != nullptr); int nextConst = 0; - return createSpvConstant(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(), + return createSpvConstantFromConstUnionArray(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(), nextConst, false); } @@ -3747,7 +3748,7 @@ spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermType if (node.getAsSymbolNode() && node.getQualifier().hasSpecConstantId()) { // this is a direct literal assigned to a layout(constant_id=) declaration int nextConst = 0; - return createSpvConstant(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(), + return createSpvConstantFromConstUnionArray(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(), nextConst, true); } else { // gl_WorkgroupSize is a special case until the front-end handles hierarchical specialization constants, @@ -3761,8 +3762,10 @@ spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermType addDecoration(dimConstId.back(), spv::DecorationSpecId, glslangIntermediate->getLocalSizeSpecId(dim)); } return builder.makeCompositeConstant(builder.makeVectorType(builder.makeUintType(32), 3), dimConstId, true); + } else if (auto* sn = node.getAsSymbolNode()){ + return createSpvConstantFromConstSubTree(sn->getConstSubtree()); } else { - spv::MissingFunctionality("specialization-constant expression trees"); + spv::MissingFunctionality("Neither a front-end constant nor a spec constant."); exit(1); return spv::NoResult; } @@ -3775,7 +3778,7 @@ spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermType // 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, bool specConstant) +spv::Id TGlslangToSpvTraverser::createSpvConstantFromConstUnionArray(const glslang::TType& glslangType, const glslang::TConstUnionArray& consts, int& nextConst, bool specConstant) { // vector of constants for SPIR-V std::vector spvConsts; @@ -3786,15 +3789,15 @@ spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangT if (glslangType.isArray()) { glslang::TType elementType(glslangType, 0); for (int i = 0; i < glslangType.getOuterArraySize(); ++i) - spvConsts.push_back(createSpvConstant(elementType, consts, nextConst, false)); + spvConsts.push_back(createSpvConstantFromConstUnionArray(elementType, consts, nextConst, false)); } else if (glslangType.isMatrix()) { glslang::TType vectorType(glslangType, 0); for (int col = 0; col < glslangType.getMatrixCols(); ++col) - spvConsts.push_back(createSpvConstant(vectorType, consts, nextConst, false)); + spvConsts.push_back(createSpvConstantFromConstUnionArray(vectorType, consts, nextConst, false)); } 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, false)); + spvConsts.push_back(createSpvConstantFromConstUnionArray(*iter->type, consts, nextConst, false)); } else if (glslangType.isVector()) { for (unsigned int i = 0; i < (unsigned int)glslangType.getVectorSize(); ++i) { bool zero = nextConst >= consts.size(); @@ -3851,6 +3854,66 @@ spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangT return builder.makeCompositeConstant(typeId, spvConsts); } +// Create constant ID from const initializer sub tree. +spv::Id TGlslangToSpvTraverser::createSpvConstantFromConstSubTree( + const glslang::TIntermTyped* subTree) { + const glslang::TType& glslangType = subTree->getType(); + spv::Id typeId = convertGlslangToSpvType(glslangType); + bool is_spec_const = subTree->getType().getQualifier().isSpecConstant(); + if (const glslang::TIntermAggregate* an = subTree->getAsAggregate()) { + // Aggregate node, we should generate OpConstantComposite or + // OpSpecConstantComposite instruction. + std::vector const_constituents; + for (auto NI = an->getSequence().begin(); NI != an->getSequence().end(); + NI++) { + const_constituents.push_back( + createSpvConstantFromConstSubTree((*NI)->getAsTyped())); + } + // Note that constructors are aggregate nodes, so expressions like: + // float x = float(y) will become an aggregate node. If 'x' is declared + // as a constant, the aggregate node representing 'float(y)' will be + // processed here. + if (builder.isVectorType(typeId) || builder.isMatrixType(typeId) || + builder.isAggregateType(typeId)) { + return builder.makeCompositeConstant(typeId, const_constituents, is_spec_const); + } else { + assert(builder.isScalarType(typeId) && const_constituents.size() == 1); + return const_constituents.front(); + } + + } else if (const glslang::TIntermBinary* bn = subTree->getAsBinaryNode()) { + // Binary operation node, we should generate OpSpecConstantOp + // This case should only happen when Specialization Constants are involved. + spv::MissingFunctionality("OpSpecConstantOp not implemented"); + return spv::NoResult; + + } else if (const glslang::TIntermUnary* un = subTree->getAsUnaryNode()) { + // Unary operation node, similar to binary operation node, should only + // happen when specialization constants are involved. + spv::MissingFunctionality("OpSpecConstantOp not implemented"); + return spv::NoResult; + + } else if (const glslang::TIntermConstantUnion* cn = subTree->getAsConstantUnion()) { + // ConstantUnion node, should redirect to + // createSpvConstantFromConstUnionArray + int nextConst = 0; + return createSpvConstantFromConstUnionArray( + glslangType, cn->getConstArray(), nextConst, is_spec_const); + + } else if (const glslang::TIntermSymbol* sn = subTree->getAsSymbolNode()) { + // Symbol node. Call getSymbolId(). This should cover both cases 1) the + // symbol has already been assigned an ID, 2) need a new ID for this + // symbol. + return getSymbolId(sn); + + } else { + spv::MissingFunctionality( + "createSpvConstantFromConstSubTree() not covered TIntermTyped* const " + "initializer subtree."); + return spv::NoResult; + } +} + // Return true if the node is a constant or symbol whose reading has no // non-trivial observable cost or effect. bool TGlslangToSpvTraverser::isTrivialLeaf(const glslang::TIntermTyped* node) diff --git a/Test/baseResults/spv.specConstantComposite.vert.out b/Test/baseResults/spv.specConstantComposite.vert.out new file mode 100644 index 00000000..5e2dfa4a --- /dev/null +++ b/Test/baseResults/spv.specConstantComposite.vert.out @@ -0,0 +1,172 @@ +spv.specConstantComposite.vert +Warning, version 450 is not yet complete; most version-specific features are present, but some are missing. + + +Linked vertex stage: + + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 106 + + Capability Shader + Capability Float64 + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Vertex 4 "main" 27 105 + Source GLSL 450 + Name 4 "main" + Name 6 "refer_primary_spec_const(" + Name 8 "refer_composite_spec_const(" + Name 10 "refer_copmosite_dot_dereference(" + Name 12 "refer_composite_bracket_dereference(" + Name 16 "refer_spec_const_array_length(" + Name 18 "declare_spec_const_in_func(" + Name 27 "color" + Name 41 "flat_struct" + MemberName 41(flat_struct) 0 "i" + MemberName 41(flat_struct) 1 "f" + MemberName 41(flat_struct) 2 "d" + MemberName 41(flat_struct) 3 "b" + Name 42 "nesting_struct" + MemberName 42(nesting_struct) 0 "nested" + MemberName 42(nesting_struct) 1 "v" + MemberName 42(nesting_struct) 2 "i" + Name 72 "indexable" + Name 76 "indexable" + Name 83 "len" + Name 105 "global_vec4_array_with_spec_length" + Decorate 21 SpecId 203 + Decorate 28 SpecId 200 + Decorate 32 SpecId 201 + Decorate 43 SpecId 202 + 2: TypeVoid + 3: TypeFunction 2 + 14: TypeInt 32 1 + 15: TypeFunction 14(int) + 20: TypeBool + 21: 20(bool) SpecConstantTrue + 24: TypeFloat 32 + 25: TypeVector 24(float) 4 + 26: TypePointer Output 25(fvec4) + 27(color): 26(ptr) Variable Output + 28: 14(int) SpecConstant 3 + 32: 24(float) SpecConstant 1078523331 + 33: 25(fvec4) SpecConstantComposite 32 32 32 32 + 36: 24(float) Constant 1133908460 + 37: 25(fvec4) SpecConstantComposite 32 32 36 36 + 40: TypeFloat 64 + 41(flat_struct): TypeStruct 14(int) 24(float) 40(float) 20(bool) +42(nesting_struct): TypeStruct 41(flat_struct) 25(fvec4) 14(int) + 43: 40(float) SpecConstant 1413754136 1074340347 + 44:41(flat_struct) SpecConstantComposite 28 32 43 21 + 45:42(nesting_struct) SpecConstantComposite 44 33 28 + 46: 14(int) Constant 2 + 51: TypeInt 32 0 + 52: 51(int) Constant 0 + 57: 51(int) Constant 5 + 58: TypeArray 24(float) 57 + 59: 24(float) Constant 1065353216 + 60: 24(float) Constant 1073741824 + 61: 24(float) Constant 1077936128 + 62: 58 SpecConstantComposite 32 32 59 60 61 + 63: 14(int) Constant 1 + 68: TypeArray 14(int) 57 + 69: 14(int) Constant 30 + 70: 68 SpecConstantComposite 28 28 63 46 69 + 71: TypePointer Function 68 + 73: TypePointer Function 14(int) + 87: 24(float) Constant 1106321080 + 88:41(flat_struct) SpecConstantComposite 69 87 43 21 + 89: 14(int) Constant 10 + 90:42(nesting_struct) SpecConstantComposite 88 37 89 + 96: 20(bool) ConstantFalse + 97:41(flat_struct) SpecConstantComposite 28 32 43 96 + 98: 24(float) Constant 1036831949 + 99: 25(fvec4) ConstantComposite 98 98 98 98 + 100:42(nesting_struct) SpecConstantComposite 97 99 28 + 101: 14(int) Constant 3000 + 102:42(nesting_struct) SpecConstantComposite 88 37 101 + 103: TypeArray 25(fvec4) 28 + 104: TypePointer Input 103 +105(global_vec4_array_with_spec_length): 104(ptr) Variable Input + 4(main): 2 Function None 3 + 5: Label + Return + FunctionEnd +6(refer_primary_spec_const(): 2 Function None 3 + 7: Label + SelectionMerge 23 None + BranchConditional 21 22 23 + 22: Label + 29: 24(float) ConvertSToF 28 + 30: 25(fvec4) Load 27(color) + 31: 25(fvec4) VectorTimesScalar 30 29 + Store 27(color) 31 + Branch 23 + 23: Label + Return + FunctionEnd +8(refer_composite_spec_const(): 2 Function None 3 + 9: Label + 34: 25(fvec4) Load 27(color) + 35: 25(fvec4) FAdd 34 33 + Store 27(color) 35 + 38: 25(fvec4) Load 27(color) + 39: 25(fvec4) FSub 38 37 + Store 27(color) 39 + Return + FunctionEnd +10(refer_copmosite_dot_dereference(): 2 Function None 3 + 11: Label + 47: 14(int) CompositeExtract 45 2 + 48: 24(float) ConvertSToF 47 + 49: 25(fvec4) Load 27(color) + 50: 25(fvec4) VectorTimesScalar 49 48 + Store 27(color) 50 + 53: 24(float) CompositeExtract 33 0 + 54: 25(fvec4) Load 27(color) + 55: 25(fvec4) CompositeConstruct 53 53 53 53 + 56: 25(fvec4) FAdd 54 55 + Store 27(color) 56 + Return + FunctionEnd +12(refer_composite_bracket_dereference(): 2 Function None 3 + 13: Label + 72(indexable): 71(ptr) Variable Function + 76(indexable): 71(ptr) Variable Function + 64: 24(float) CompositeExtract 62 1 + 65: 25(fvec4) Load 27(color) + 66: 25(fvec4) CompositeConstruct 64 64 64 64 + 67: 25(fvec4) FSub 65 66 + Store 27(color) 67 + Store 72(indexable) 70 + 74: 73(ptr) AccessChain 72(indexable) 28 + 75: 14(int) Load 74 + Store 76(indexable) 70 + 77: 73(ptr) AccessChain 76(indexable) 75 + 78: 14(int) Load 77 + 79: 24(float) ConvertSToF 78 + 80: 25(fvec4) Load 27(color) + 81: 25(fvec4) CompositeConstruct 79 79 79 79 + 82: 25(fvec4) FDiv 80 81 + Store 27(color) 82 + Return + FunctionEnd +16(refer_spec_const_array_length(): 14(int) Function None 15 + 17: Label + 83(len): 73(ptr) Variable Function + Store 83(len) 28 + 84: 14(int) Load 83(len) + ReturnValue 84 + FunctionEnd +18(declare_spec_const_in_func(): 2 Function None 3 + 19: Label + 91: 14(int) CompositeExtract 90 2 + 92: 24(float) ConvertSToF 91 + 93: 25(fvec4) Load 27(color) + 94: 25(fvec4) CompositeConstruct 92 92 92 92 + 95: 25(fvec4) FDiv 93 94 + Store 27(color) 95 + Return + FunctionEnd diff --git a/Test/spv.specConstantComposite.vert b/Test/spv.specConstantComposite.vert new file mode 100644 index 00000000..4450ddd6 --- /dev/null +++ b/Test/spv.specConstantComposite.vert @@ -0,0 +1,93 @@ +#version 450 + +// constant_id specified scalar spec constants +layout(constant_id = 200) const int spec_int = 3; +layout(constant_id = 201) const float spec_float = 3.14; +layout(constant_id = 202) const + double spec_double = 3.1415926535897932384626433832795; +layout(constant_id = 203) const bool spec_bool = true; + +const float cast_spec_float = float(spec_float); + +// Flat struct +struct flat_struct { + int i; + float f; + double d; + bool b; +}; + +// Nesting struct +struct nesting_struct { + flat_struct nested; + vec4 v; + int i; +}; + +// Expect OpSpecConstantComposite +// Flat struct initializer +const flat_struct spec_flat_struct_all_spec = {spec_int, spec_float, + spec_double, spec_bool}; +const flat_struct spec_flat_struct_partial_spec = {30, 30.14, spec_double, + spec_bool}; + +// Nesting struct initializer +const nesting_struct nesting_struct_ctor = { + {spec_int, spec_float, spec_double, false}, + vec4(0.1, 0.1, 0.1, 0.1), + spec_int}; + +// Vector constructor +const vec4 spec_vec4_all_spec = + vec4(spec_float, spec_float, spec_float, spec_float); +const vec4 spec_vec4_partial_spec = + vec4(spec_float, spec_float, 300.14, 300.14); + +// Struct nesting constructor +const nesting_struct spec_nesting_struct_all_spec = { + spec_flat_struct_all_spec, spec_vec4_all_spec, spec_int}; +const nesting_struct spec_nesting_struct_partial_spec = { + spec_flat_struct_partial_spec, spec_vec4_partial_spec, 3000}; + +const float spec_float_array[5] = {spec_float, spec_float, 1.0, 2.0, 3.0}; +const int spec_int_array[5] = {spec_int, spec_int, 1, 2, 30}; + +// global_vec4_array_with_spec_length is not a spec constant, but its array +// size is. When calling global_vec4_array_with_spec_length.length(), A +// TIntermSymbol Node shoule be returned, instead of a TIntermConstantUnion +// node which represents a known constant value. +in vec4 global_vec4_array_with_spec_length[spec_int]; + +out vec4 color; + +void refer_primary_spec_const() { + if (spec_bool) color *= spec_int; +} + +void refer_composite_spec_const() { + color += spec_vec4_all_spec; + color -= spec_vec4_partial_spec; +} + +void refer_copmosite_dot_dereference() { + color *= spec_nesting_struct_all_spec.i; + color += spec_vec4_all_spec.x; +} + +void refer_composite_bracket_dereference() { + color -= spec_float_array[1]; + color /= spec_int_array[spec_int_array[spec_int]]; +} + +int refer_spec_const_array_length() { + int len = global_vec4_array_with_spec_length.length(); + return len; +} + +void declare_spec_const_in_func() { + const nesting_struct spec_const_declared_in_func = { + spec_flat_struct_partial_spec, spec_vec4_partial_spec, 10}; + color /= spec_const_declared_in_func.i; +} + +void main() {} diff --git a/Test/test-spirv-list b/Test/test-spirv-list index 0e223674..4534132f 100644 --- a/Test/test-spirv-list +++ b/Test/test-spirv-list @@ -102,6 +102,7 @@ spv.pushConstant.vert spv.subpass.frag spv.specConstant.vert spv.specConstant.comp +spv.specConstantComposite.vert # GLSL-level semantics vulkan.frag vulkan.vert diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h index 05681c9a..bff7e9a9 100644 --- a/glslang/Include/Types.h +++ b/glslang/Include/Types.h @@ -1223,6 +1223,7 @@ public: virtual int getMatrixCols() const { return matrixCols; } virtual int getMatrixRows() const { return matrixRows; } virtual int getOuterArraySize() const { return arraySizes->getOuterSize(); } + virtual TIntermTyped* getOuterArrayNode() const { return arraySizes->getOuterNode(); } virtual int getCumulativeArraySize() const { return arraySizes->getCumulativeSize(); } virtual bool isArrayOfArrays() const { return arraySizes != nullptr && arraySizes->getNumDims() > 1; } virtual int getImplicitArraySize() const { return arraySizes->getImplicitSize(); } diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 13296318..5de89e1b 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -484,7 +484,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn TIntermTyped* result = nullptr; int indexValue = 0; - if (index->getQualifier().isConstant()) { + if (index->getQualifier().isFrontEndConstant()) { indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst(); checkIndex(loc, base->getType(), indexValue); } @@ -503,7 +503,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn if (base->getAsSymbolNode() && isIoResizeArray(base->getType())) handleIoResizeArrayAccess(loc, base); - if (index->getQualifier().isConstant()) { + if (index->getQualifier().isFrontEndConstant()) { if (base->getType().isImplicitlySizedArray()) updateImplicitArraySize(loc, base, indexValue); result = intermediate.addIndex(EOpIndexDirect, base, index, loc); @@ -541,10 +541,15 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn } else { // Insert valid dereferenced result TType newType(base->getType(), 0); // dereferenced type - if (base->getType().getQualifier().isFrontEndConstant() && index->getQualifier().isFrontEndConstant()) + if (base->getType().getQualifier().isConstant() && index->getQualifier().isConstant()) { newType.getQualifier().storage = EvqConst; - else + // If base or index is a specialization constant, the result should also be a specialization constant. + if (base->getType().getQualifier().isSpecConstant() || index->getQualifier().isSpecConstant()) { + newType.getQualifier().makeSpecConstant(); + } + } else { newType.getQualifier().makePartialTemporary(); + } result->setType(newType); if (anyIndexLimits) @@ -1226,6 +1231,11 @@ TIntermTyped* TParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction else error(loc, "", function->getName().c_str(), "array must be declared with a size before using this method"); } + } else if (type.getOuterArrayNode()) { + // If the array's outer size is specified by an intermediate node, it means the array's length + // was specified by a specialization constant. In such a case, we should return the node of the + // specialization constants to represent the length. + return type.getOuterArrayNode(); } else length = type.getOuterArraySize(); } else if (type.isMatrix()) @@ -5110,7 +5120,8 @@ TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* // We don't know "top down" whether type is a specialization constant, // but a const becomes a specialization constant if any of its children are. - bool specConst = false; + bool hasSpecConst = false; + bool isConstConstrutor = true; for (TIntermSequence::iterator p = sequenceVector.begin(); p != sequenceVector.end(); p++, paramCount++) { @@ -5123,14 +5134,16 @@ TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* if (newNode) { *p = newNode; + if (! newNode->getType().getQualifier().isConstant()) + isConstConstrutor = false; if (newNode->getType().getQualifier().isSpecConstant()) - specConst = true; + hasSpecConst = true; } else return nullptr; } TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc); - if (constructor->getType().getQualifier().isConstant() && specConst) + if (isConstConstrutor && hasSpecConst) constructor->getWritableType().getQualifier().makeSpecConstant(); return constructor;