diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 1a8ba449..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); @@ -719,6 +720,7 @@ void TGlslangToSpvTraverser::dumpSpv(std::vector& out) for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it) entryPoint->addIdOperand(*it); + builder.eliminateDeadDecorations(); builder.dump(out); } @@ -1519,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); @@ -1628,8 +1630,8 @@ 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) { - return createSpvSpecConstant(*node); + if (node->getQualifier().isConstant()) { + return createSpvConstant(*node); } // Now, handle actual variables @@ -3434,11 +3436,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; @@ -3728,15 +3731,15 @@ 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().storage == glslang::EvqConst); + assert(node.getQualifier().isConstant()); if (! node.getQualifier().specConstant) { // 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); } @@ -3745,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, @@ -3759,8 +3762,11 @@ 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; } } @@ -3772,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; @@ -3783,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(); @@ -3848,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/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp index ca650185..8787082b 100644 --- a/SPIRV/SpvBuilder.cpp +++ b/SPIRV/SpvBuilder.cpp @@ -2234,6 +2234,45 @@ 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 (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 (std::vector::const_iterator bi = f->getBlocks().cbegin(); + bi != f->getBlocks().cend(); bi++) + { + Block* b = *bi; + if (!reachable_blocks.count(b)) + { + for (std::vector >::const_iterator ii = + b->getInstructions().cbegin(); + ii != b->getInstructions().cend(); ii++) + { + Instruction* i = ii->get(); + 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 83b4cbb5..cb4daff1 100755 --- a/SPIRV/SpvBuilder.h +++ b/SPIRV/SpvBuilder.h @@ -535,6 +535,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 { 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/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/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/baseResults/spv.400.frag.out b/Test/baseResults/spv.400.frag.out index a851500c..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 @@ -719,28 +731,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 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/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/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/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/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/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-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/Test/test-spirv-list b/Test/test-spirv-list index 009e0453..4534132f 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 @@ -101,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 c88bca67..bff7e9a9 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; @@ -727,6 +740,7 @@ public: } void makeSpecConstant() { + storage = EvqConst; specConstant = true; } static const char* getLayoutPackingString(TLayoutPacking packing) @@ -1209,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/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 bafcb917..777de2d6 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -61,25 +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, + TIntermTyped* constSubtree, const TSourceLoc& loc) { TIntermSymbol* node = new TIntermSymbol(id, name, type); node->setLoc(loc); + node->setConstArray(constArray); + node->setConstSubtree(constSubtree); return node; } -TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TConstUnionArray& constArray, const TSourceLoc& loc) +TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable) { - TIntermSymbol* node = addSymbol(id, name, type, loc); - node->setConstArray(constArray); + glslang::TSourceLoc loc; // just a null location + loc.init(); - return node; + return addSymbol(variable, loc); } 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, nullptr, loc); } // @@ -252,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: @@ -281,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(); @@ -474,6 +485,7 @@ TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TInt case EOpSub: case EOpMul: case EOpDiv: + case EOpMod: case EOpVectorTimesScalar: case EOpVectorTimesMatrix: @@ -605,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; } @@ -813,7 +831,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); @@ -872,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); @@ -1017,8 +1034,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..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().storage == EvqConst) { + if (index->getQualifier().isFrontEndConstant()) { 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().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().storage == EvqConst && index->getQualifier().storage == EvqConst) + if (base->getType().getQualifier().isConstant() && index->getQualifier().isConstant()) { newType.getQualifier().storage = EvqConst; - else - newType.getQualifier().storage = EvqTemporary; + // 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) @@ -587,7 +592,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 @@ -820,6 +825,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 +845,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(); @@ -849,7 +860,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); @@ -1017,7 +1028,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; @@ -1220,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()) @@ -2039,7 +2055,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 +2220,7 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T int size = 0; bool constType = true; + bool specConstType = false; // value is only valid if constType is true bool full = false; bool overFull = false; bool matrixInMatrix = false; @@ -2232,12 +2249,18 @@ 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 = true; } - if (constType) - type.getQualifier().storage = EvqConst; + if (constType) { + if (specConstType) + type.getQualifier().makeSpecConstant(); + else + type.getQualifier().storage = EvqConst; + } if (type.isArray()) { if (function.getParamCount() == 0) { @@ -3147,7 +3170,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 +4871,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 +4891,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 +4918,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, ""); @@ -4907,14 +4931,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().storage = EvqTemporary; + 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"); @@ -5075,12 +5112,17 @@ 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 hasSpecConst = false; + bool isConstConstrutor = true; + for (TIntermSequence::iterator p = sequenceVector.begin(); p != sequenceVector.end(); p++, paramCount++) { if (type.isArray()) @@ -5090,13 +5132,19 @@ 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().isConstant()) + isConstConstrutor = false; + if (newNode->getType().getQualifier().isSpecConstant()) + hasSpecConst = true; + } else return nullptr; } TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc); + if (isConstConstrutor && hasSpecConst) + constructor->getWritableType().getQualifier().makeSpecConstant(); return constructor; } 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/Scan.h b/glslang/MachineIndependent/Scan.h index 936b99f1..219049be 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,57 @@ 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 setFile(const char* filename, size_t i) + { + if (i == getLastValidSourceIndex()) { + logicalSourceLoc.name = filename; + } + loc[i].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 +247,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/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 b52551ff..eb6c30cd 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); @@ -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 99267475..5e5a2e77 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&, 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); diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp index 9a33a776..b9d00c8e 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); + } } } } @@ -867,12 +871,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: 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..b68c6916 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,21 +431,40 @@ 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), + 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: @@ -456,16 +476,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 +518,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 +543,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/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) diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index 702b66f4..c9182618 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -292,25 +292,90 @@ 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"; + static const IncludeResult unexpectedIncludeResult = + {"", unexpected_include, sizeof(unexpected_include) - 1, nullptr}; + return new IncludeResult(unexpectedIncludeResult); + } + 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 +383,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();