From e18fd20d5c5eab5f4cd80596333f3333c4409e72 Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Tue, 30 Jan 2018 11:01:39 -0700 Subject: [PATCH] HLSL: Refactor attribute implementation. - make it sharable with GLSL - correct the case insensitivity - remove the map; queries are not needed, all entries need processing - make it easier to build bottom up (will help GLSL parsing) - support semantic checking and reporting - allow front-end dependent semantics and attribute name mapping --- SPIRV/GlslangToSpv.cpp | 51 ++- Test/baseResults/hlsl.numthreads.comp.out | 30 +- Test/hlsl.numthreads.comp | 7 +- glslang/CMakeLists.txt | 2 + glslang/Include/ConstantUnion.h | 3 + glslang/Include/intermediate.h | 69 ++- glslang/MachineIndependent/Intermediate.cpp | 18 +- glslang/MachineIndependent/attribute.cpp | 86 ++++ glslang/MachineIndependent/attribute.h | 97 ++++ .../MachineIndependent/localintermediate.h | 9 +- gtests/Hlsl.FromFile.cpp | 2 +- hlsl/hlslAttributes.cpp | 142 ++---- hlsl/hlslAttributes.h | 79 +--- hlsl/hlslGrammar.cpp | 91 ++-- hlsl/hlslGrammar.h | 13 +- hlsl/hlslParseHelper.cpp | 420 ++++++++++-------- hlsl/hlslParseHelper.h | 19 +- 17 files changed, 638 insertions(+), 500 deletions(-) create mode 100644 glslang/MachineIndependent/attribute.cpp create mode 100644 glslang/MachineIndependent/attribute.h diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 64bce356f..b7b6597dc 100755 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -129,8 +129,9 @@ protected: spv::Decoration TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier); spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable, bool memberDeclaration); spv::ImageFormat TranslateImageFormat(const glslang::TType& type); - spv::SelectionControlMask TranslateSelectionControl(glslang::TSelectionControl) const; - spv::LoopControlMask TranslateLoopControl(glslang::TLoopControl) const; + spv::SelectionControlMask TranslateSelectionControl(const glslang::TIntermSelection&) const; + spv::SelectionControlMask TranslateSwitchControl(const glslang::TIntermSwitch&) const; + spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&) const; spv::StorageClass TranslateStorageClass(const glslang::TType&); spv::Id createSpvVariable(const glslang::TIntermSymbol*); spv::Id getSampledType(const glslang::TSampler&); @@ -748,26 +749,34 @@ spv::ImageFormat TGlslangToSpvTraverser::TranslateImageFormat(const glslang::TTy } } -spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSelectionControl(glslang::TSelectionControl selectionControl) const +spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSelectionControl(const glslang::TIntermSelection& selectionNode) const { - switch (selectionControl) { - case glslang::ESelectionControlNone: return spv::SelectionControlMaskNone; - case glslang::ESelectionControlFlatten: return spv::SelectionControlFlattenMask; - case glslang::ESelectionControlDontFlatten: return spv::SelectionControlDontFlattenMask; - default: return spv::SelectionControlMaskNone; - } + if (selectionNode.getFlatten()) + return spv::SelectionControlFlattenMask; + if (selectionNode.getDontFlatten()) + return spv::SelectionControlDontFlattenMask; + return spv::SelectionControlMaskNone; } -spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(glslang::TLoopControl loopControl) const +spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSwitchControl(const glslang::TIntermSwitch& switchNode) const { - switch (loopControl) { - case glslang::ELoopControlNone: return spv::LoopControlMaskNone; - case glslang::ELoopControlUnroll: return spv::LoopControlUnrollMask; - case glslang::ELoopControlDontUnroll: return spv::LoopControlDontUnrollMask; - // TODO: DependencyInfinite - // TODO: DependencyLength - default: return spv::LoopControlMaskNone; - } + if (switchNode.getFlatten()) + return spv::SelectionControlFlattenMask; + if (switchNode.getDontFlatten()) + return spv::SelectionControlDontFlattenMask; + return spv::SelectionControlMaskNone; +} + +spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode) const +{ + spv::LoopControlMask control = spv::LoopControlMaskNone; + + if (loopNode.getDontUnroll()) + control = control | spv::LoopControlDontUnrollMask; + if (loopNode.getUnroll()) + control = control | spv::LoopControlUnrollMask; + + return control; } // Translate glslang type to SPIR-V storage class. @@ -2026,7 +2035,7 @@ bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang node->getCondition()->traverse(this); // Selection control: - const spv::SelectionControlMask control = TranslateSelectionControl(node->getSelectionControl()); + const spv::SelectionControlMask control = TranslateSelectionControl(*node); // make an "if" based on the value created by the condition spv::Builder::If ifBuilder(accessChainLoad(node->getCondition()->getType()), control, builder); @@ -2068,7 +2077,7 @@ bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::T spv::Id selector = accessChainLoad(node->getCondition()->getAsTyped()->getType()); // Selection control: - const spv::SelectionControlMask control = TranslateSelectionControl(node->getSelectionControl()); + const spv::SelectionControlMask control = TranslateSwitchControl(*node); // browse the children to sort out code segments int defaultSegment = -1; @@ -2128,7 +2137,7 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn builder.createBranch(&blocks.head); // Loop control: - const spv::LoopControlMask control = TranslateLoopControl(node->getLoopControl()); + const spv::LoopControlMask control = TranslateLoopControl(*node); // TODO: dependency length diff --git a/Test/baseResults/hlsl.numthreads.comp.out b/Test/baseResults/hlsl.numthreads.comp.out index c6ec011d2..d92b8600c 100644 --- a/Test/baseResults/hlsl.numthreads.comp.out +++ b/Test/baseResults/hlsl.numthreads.comp.out @@ -1,20 +1,20 @@ hlsl.numthreads.comp Shader version: 500 -local_size = (4, 4, 2) +local_size = (1, 4, 8) 0:? Sequence 0:4 Function Definition: main(vu3; ( temp void) 0:4 Function Parameters: 0:4 'tid' ( in 3-component vector of uint) -0:9 Function Definition: @main_aux1(vu3; ( temp void) +0:9 Function Definition: @main_aux2(vu3; ( temp void) 0:9 Function Parameters: 0:9 'tid' ( in 3-component vector of uint) -0:9 Function Definition: main_aux1( ( temp void) +0:9 Function Definition: main_aux2( ( temp void) 0:9 Function Parameters: 0:? Sequence 0:9 move second child to first child ( temp 3-component vector of uint) 0:? 'tid' ( temp 3-component vector of uint) 0:? 'tid' ( in 3-component vector of uint GlobalInvocationID) -0:9 Function Call: @main_aux1(vu3; ( temp void) +0:9 Function Call: @main_aux2(vu3; ( temp void) 0:? 'tid' ( temp 3-component vector of uint) 0:? Linker Objects 0:? 'tid' ( in 3-component vector of uint GlobalInvocationID) @@ -24,21 +24,21 @@ Linked compute stage: Shader version: 500 -local_size = (4, 4, 2) +local_size = (1, 4, 8) 0:? Sequence 0:4 Function Definition: main(vu3; ( temp void) 0:4 Function Parameters: 0:4 'tid' ( in 3-component vector of uint) -0:9 Function Definition: @main_aux1(vu3; ( temp void) +0:9 Function Definition: @main_aux2(vu3; ( temp void) 0:9 Function Parameters: 0:9 'tid' ( in 3-component vector of uint) -0:9 Function Definition: main_aux1( ( temp void) +0:9 Function Definition: main_aux2( ( temp void) 0:9 Function Parameters: 0:? Sequence 0:9 move second child to first child ( temp 3-component vector of uint) 0:? 'tid' ( temp 3-component vector of uint) 0:? 'tid' ( in 3-component vector of uint GlobalInvocationID) -0:9 Function Call: @main_aux1(vu3; ( temp void) +0:9 Function Call: @main_aux2(vu3; ( temp void) 0:? 'tid' ( temp 3-component vector of uint) 0:? Linker Objects 0:? 'tid' ( in 3-component vector of uint GlobalInvocationID) @@ -50,13 +50,13 @@ local_size = (4, 4, 2) Capability Shader 1: ExtInstImport "GLSL.std.450" MemoryModel Logical GLSL450 - EntryPoint GLCompute 4 "main_aux1" 18 - ExecutionMode 4 LocalSize 4 4 2 + EntryPoint GLCompute 4 "main_aux2" 18 + ExecutionMode 4 LocalSize 1 4 8 Source HLSL 500 - Name 4 "main_aux1" + Name 4 "main_aux2" Name 11 "main(vu3;" Name 10 "tid" - Name 14 "@main_aux1(vu3;" + Name 14 "@main_aux2(vu3;" Name 13 "tid" Name 16 "tid" Name 18 "tid" @@ -70,7 +70,7 @@ local_size = (4, 4, 2) 9: TypeFunction 2 8(ptr) 17: TypePointer Input 7(ivec3) 18(tid): 17(ptr) Variable Input - 4(main_aux1): 2 Function None 3 + 4(main_aux2): 2 Function None 3 5: Label 16(tid): 8(ptr) Variable Function 20(param): 8(ptr) Variable Function @@ -78,7 +78,7 @@ local_size = (4, 4, 2) Store 16(tid) 19 21: 7(ivec3) Load 16(tid) Store 20(param) 21 - 22: 2 FunctionCall 14(@main_aux1(vu3;) 20(param) + 22: 2 FunctionCall 14(@main_aux2(vu3;) 20(param) Return FunctionEnd 11(main(vu3;): 2 Function None 9 @@ -86,7 +86,7 @@ local_size = (4, 4, 2) 12: Label Return FunctionEnd -14(@main_aux1(vu3;): 2 Function None 9 +14(@main_aux2(vu3;): 2 Function None 9 13(tid): 8(ptr) FunctionParameter 15: Label Return diff --git a/Test/hlsl.numthreads.comp b/Test/hlsl.numthreads.comp index fcc97f379..0871d3f74 100644 --- a/Test/hlsl.numthreads.comp +++ b/Test/hlsl.numthreads.comp @@ -4,11 +4,8 @@ void main(uint3 tid : SV_DispatchThreadID ) { } -[numTHreaDs(4,4,2)] // case insensitive -void main_aux1(uint3 tid : SV_DispatchThreadID ) +[numthreads(1,4,8)] +void main_aux2(uint3 tid : SV_DispatchThreadID ) { } -[numthreads(1,4,8)] -void main_aux2(uint3 tid : SV_DispatchThreadID ); - diff --git a/glslang/CMakeLists.txt b/glslang/CMakeLists.txt index ac3973373..7a50ab678 100644 --- a/glslang/CMakeLists.txt +++ b/glslang/CMakeLists.txt @@ -9,6 +9,7 @@ endif(WIN32) set(SOURCES MachineIndependent/glslang.y MachineIndependent/glslang_tab.cpp + MachineIndependent/attribute.cpp MachineIndependent/Constant.cpp MachineIndependent/iomapper.cpp MachineIndependent/InfoSink.cpp @@ -51,6 +52,7 @@ set(HEADERS Include/revision.h Include/ShHandle.h Include/Types.h + MachineIndependent/attribute.h MachineIndependent/glslang_tab.cpp.h MachineIndependent/gl_types.h MachineIndependent/Initialize.h diff --git a/glslang/Include/ConstantUnion.h b/glslang/Include/ConstantUnion.h index eb28f21c3..58c6094ea 100644 --- a/glslang/Include/ConstantUnion.h +++ b/glslang/Include/ConstantUnion.h @@ -37,6 +37,9 @@ #ifndef _CONSTANT_UNION_INCLUDED_ #define _CONSTANT_UNION_INCLUDED_ +#include "../Include/Common.h" +#include "../Include/BaseTypes.h" + namespace glslang { class TConstUnion { diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h index 587601957..2aa4d0483 100644 --- a/glslang/Include/intermediate.h +++ b/glslang/Include/intermediate.h @@ -819,7 +819,7 @@ public: virtual glslang::TIntermMethod* getAsMethodNode() { return 0; } virtual glslang::TIntermSymbol* getAsSymbolNode() { return 0; } virtual glslang::TIntermBranch* getAsBranchNode() { return 0; } - virtual glslang::TIntermLoop* getAsLoopNode() { return 0; } + virtual glslang::TIntermLoop* getAsLoopNode() { return 0; } virtual const glslang::TIntermTyped* getAsTyped() const { return 0; } virtual const glslang::TIntermOperator* getAsOperator() const { return 0; } @@ -832,7 +832,7 @@ public: virtual const glslang::TIntermMethod* getAsMethodNode() const { return 0; } virtual const glslang::TIntermSymbol* getAsSymbolNode() const { return 0; } virtual const glslang::TIntermBranch* getAsBranchNode() const { return 0; } - virtual const glslang::TIntermLoop* getAsLoopNode() const { return 0; } + virtual const glslang::TIntermLoop* getAsLoopNode() const { return 0; } virtual ~TIntermNode() { } protected: @@ -885,24 +885,6 @@ protected: TType type; }; -// -// Selection control hints -// -enum TSelectionControl { - ESelectionControlNone, - ESelectionControlFlatten, - ESelectionControlDontFlatten, -}; - -// -// Loop control hints -// -enum TLoopControl { - ELoopControlNone, - ELoopControlUnroll, - ELoopControlDontUnroll, -}; - // // Handle for, do-while, and while loops. // @@ -913,26 +895,30 @@ public: test(aTest), terminal(aTerminal), first(testFirst), - control(ELoopControlNone) + unroll(false), + dontUnroll(false) { } - virtual TIntermLoop* getAsLoopNode() { return this; } - virtual const TIntermLoop* getAsLoopNode() const { return this; } + virtual TIntermLoop* getAsLoopNode() { return this; } + virtual const TIntermLoop* getAsLoopNode() const { return this; } virtual void traverse(TIntermTraverser*); TIntermNode* getBody() const { return body; } TIntermTyped* getTest() const { return test; } TIntermTyped* getTerminal() const { return terminal; } bool testFirst() const { return first; } - void setLoopControl(TLoopControl c) { control = c; } - TLoopControl getLoopControl() const { return control; } + void setUnroll() { unroll = true; } + void setDontUnroll() { dontUnroll = true; } + bool getUnroll() const { return unroll; } + bool getDontUnroll() const { return dontUnroll; } protected: TIntermNode* body; // code to loop over TIntermTyped* test; // exit condition associated with loop, could be 0 for 'for' loops TIntermTyped* terminal; // exists for for-loops bool first; // true for while and for, not for do-while - TLoopControl control; // loop control hint + bool unroll; // true if unroll requested + bool dontUnroll; // true if request to not unroll }; // @@ -1343,22 +1329,29 @@ protected: class TIntermSelection : public TIntermTyped { public: TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB) : - TIntermTyped(EbtVoid), condition(cond), trueBlock(trueB), falseBlock(falseB), control(ESelectionControlNone) {} + TIntermTyped(EbtVoid), condition(cond), trueBlock(trueB), falseBlock(falseB), + flatten(false), dontFlatten(false) {} TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB, const TType& type) : - TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB), control(ESelectionControlNone) {} + TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB), + flatten(false), dontFlatten(false) {} virtual void traverse(TIntermTraverser*); virtual TIntermTyped* getCondition() const { return condition; } virtual TIntermNode* getTrueBlock() const { return trueBlock; } virtual TIntermNode* getFalseBlock() const { return falseBlock; } virtual TIntermSelection* getAsSelectionNode() { return this; } virtual const TIntermSelection* getAsSelectionNode() const { return this; } - void setSelectionControl(TSelectionControl c) { control = c; } - TSelectionControl getSelectionControl() const { return control; } + + void setFlatten() { flatten = true; } + void setDontFlatten() { dontFlatten = true; } + bool getFlatten() const { return flatten; } + bool getDontFlatten() const { return dontFlatten; } + protected: TIntermTyped* condition; TIntermNode* trueBlock; TIntermNode* falseBlock; - TSelectionControl control; // selection control hint + bool flatten; // true if flatten requested + bool dontFlatten; // true if requested to not flatten }; // @@ -1369,18 +1362,24 @@ protected: // class TIntermSwitch : public TIntermNode { public: - TIntermSwitch(TIntermTyped* cond, TIntermAggregate* b) : condition(cond), body(b), control(ESelectionControlNone) { } + TIntermSwitch(TIntermTyped* cond, TIntermAggregate* b) : condition(cond), body(b), + flatten(false), dontFlatten(false) {} virtual void traverse(TIntermTraverser*); virtual TIntermNode* getCondition() const { return condition; } virtual TIntermAggregate* getBody() const { return body; } virtual TIntermSwitch* getAsSwitchNode() { return this; } virtual const TIntermSwitch* getAsSwitchNode() const { return this; } - void setSelectionControl(TSelectionControl c) { control = c; } - TSelectionControl getSelectionControl() const { return control; } + + void setFlatten() { flatten = true; } + void setDontFlatten() { dontFlatten = true; } + bool getFlatten() const { return flatten; } + bool getDontFlatten() const { return dontFlatten; } + protected: TIntermTyped* condition; TIntermAggregate* body; - TSelectionControl control; // selection control hint + bool flatten; // true if flatten requested + bool dontFlatten; // true if requested to not flatten }; enum TVisit diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp index 21899abe9..6cec765b9 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -1614,7 +1614,7 @@ TIntermAggregate* TIntermediate::makeAggregate(const TSourceLoc& loc) // // Returns the selection node created. // -TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc, TSelectionControl control) +TIntermSelection* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc) { // // Don't prune the false path for compile-time constants; it's needed @@ -1623,7 +1623,6 @@ TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair no TIntermSelection* node = new TIntermSelection(cond, nodePair.node1, nodePair.node2); node->setLoc(loc); - node->setSelectionControl(control); return node; } @@ -1666,12 +1665,13 @@ TIntermTyped* TIntermediate::addMethod(TIntermTyped* object, const TType& type, // // Returns the selection node created, or nullptr if one could not be. // -TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc& loc, TSelectionControl control) +TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, + const TSourceLoc& loc) { // If it's void, go to the if-then-else selection() if (trueBlock->getBasicType() == EbtVoid && falseBlock->getBasicType() == EbtVoid) { TIntermNodePair pair = { trueBlock, falseBlock }; - return addSelection(cond, pair, loc, control); + return addSelection(cond, pair, loc); } // @@ -1909,11 +1909,11 @@ const TIntermTyped* TIntermediate::findLValueBase(const TIntermTyped* node, bool // // Create while and do-while loop nodes. // -TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TLoopControl control) +TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, + const TSourceLoc& loc) { TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst); node->setLoc(loc); - node->setLoopControl(control); return node; } @@ -1921,11 +1921,11 @@ TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TInte // // Create a for-loop sequence. // -TIntermAggregate* TIntermediate::addForLoop(TIntermNode* body, TIntermNode* initializer, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TLoopControl control) +TIntermAggregate* TIntermediate::addForLoop(TIntermNode* body, TIntermNode* initializer, TIntermTyped* test, + TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TIntermLoop*& node) { - TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst); + node = new TIntermLoop(body, test, terminal, testFirst); node->setLoc(loc); - node->setLoopControl(control); // make a sequence of the initializer and statement, but try to reuse the // aggregate already created for whatever is in the initializer, if there is one diff --git a/glslang/MachineIndependent/attribute.cpp b/glslang/MachineIndependent/attribute.cpp new file mode 100644 index 000000000..3593e560f --- /dev/null +++ b/glslang/MachineIndependent/attribute.cpp @@ -0,0 +1,86 @@ +// +// Copyright (C) 2017 LunarG, Inc. +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "attribute.h" +#include "../Include/intermediate.h" +#include "ParseHelper.h" + +namespace glslang { + + // extract integers out of attribute arguments stored in attribute aggregate + bool TAttributeArgs::getInt(int& value, int argNum) const + { + const TConstUnion* intConst = getConstUnion(EbtInt, argNum); + + if (intConst == nullptr) + return false; + + value = intConst->getIConst(); + return true; + }; + + // extract strings out of attribute arguments stored in attribute aggregate. + // convert to lower case if converToLower is true (for case-insensitive compare convenience) + bool TAttributeArgs::getString(TString& value, int argNum, bool convertToLower) const + { + const TConstUnion* stringConst = getConstUnion(EbtString, argNum); + + if (stringConst == nullptr) + return false; + + value = *stringConst->getSConst(); + + // Convenience. + if (convertToLower) + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + + return true; + }; + + // Helper to get attribute const union. Returns nullptr on failure. + const TConstUnion* TAttributeArgs::getConstUnion(TBasicType basicType, int argNum) const + { + if (argNum >= args->getSequence().size()) + return nullptr; + + const TConstUnion* constVal = &args->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0]; + if (constVal == nullptr || constVal->getType() != basicType) + return nullptr; + + return constVal; + } + +} // end namespace glslang diff --git a/glslang/MachineIndependent/attribute.h b/glslang/MachineIndependent/attribute.h new file mode 100644 index 000000000..6a9529058 --- /dev/null +++ b/glslang/MachineIndependent/attribute.h @@ -0,0 +1,97 @@ +// +// Copyright (C) 2017 LunarG, Inc. +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _ATTRIBUTE_INCLUDED_ +#define _ATTRIBUTE_INCLUDED_ + +#include "../Include/Common.h" +#include "../Include/ConstantUnion.h" + +namespace glslang { + + enum TAttributeType { + EatNone, + EatAllow_uav_condition, + EatBranch, + EatCall, + EatDomain, + EatEarlyDepthStencil, + EatFastOpt, + EatFlatten, + EatForceCase, + EatInstance, + EatMaxTessFactor, + EatNumThreads, + EatMaxVertexCount, + EatOutputControlPoints, + EatOutputTopology, + EatPartitioning, + EatPatchConstantFunc, + EatPatchSize, + EatUnroll, + EatLoop, + EatBinding, + EatGlobalBinding, + EatLocation, + EatInputAttachment, + EatBuiltIn, + EatPushConstant, + EatConstantId + }; + + class TIntermAggregate; + + struct TAttributeArgs { + TAttributeType name; + TIntermAggregate* args; + + // Obtain attribute as integer + // Return false if it cannot be obtained + bool getInt(int& value, int argNum = 0) const; + + // Obtain attribute as string, with optional to-lower transform + // Return false if it cannot be obtained + bool getString(TString& value, int argNum = 0, bool convertToLower = true) const; + + protected: + const TConstUnion* getConstUnion(TBasicType basicType, int argNum) const; + }; + + typedef TList TAttributes; + +} // end namespace glslang + +#endif // _ATTRIBUTE_INCLUDED_ diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index 4d48c68d4..b0ef5fd03 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -420,8 +420,8 @@ public: TIntermAggregate* makeAggregate(const TSourceLoc&); TIntermTyped* setAggregateOperator(TIntermNode*, TOperator, const TType& type, TSourceLoc); bool areAllChildConst(TIntermAggregate* aggrNode); - TIntermTyped* addSelection(TIntermTyped* cond, TIntermNodePair code, const TSourceLoc&, TSelectionControl = ESelectionControlNone); - TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc&, TSelectionControl = ESelectionControlNone); + TIntermSelection* addSelection(TIntermTyped* cond, TIntermNodePair code, const TSourceLoc&); + TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc&); TIntermTyped* addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); TIntermTyped* addMethod(TIntermTyped*, const TType&, const TString*, const TSourceLoc&); TIntermConstantUnion* addConstantUnion(const TConstUnionArray&, const TType&, const TSourceLoc&, bool literal = false) const; @@ -439,8 +439,9 @@ public: TIntermConstantUnion* addConstantUnion(const TString*, const TSourceLoc&, bool literal = false) const; TIntermTyped* promoteConstantUnion(TBasicType, TIntermConstantUnion*) const; bool parseConstTree(TIntermNode*, TConstUnionArray, TOperator, const TType&, bool singleConstantParam = false); - TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&, TLoopControl = ELoopControlNone); - TIntermAggregate* addForLoop(TIntermNode*, TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&, TLoopControl = ELoopControlNone); + TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&); + TIntermAggregate* addForLoop(TIntermNode*, TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, + const TSourceLoc&, TIntermLoop*&); TIntermBranch* addBranch(TOperator, const TSourceLoc&); TIntermBranch* addBranch(TOperator, TIntermTyped*, const TSourceLoc&); template TIntermTyped* addSwizzle(TSwizzleSelectors&, const TSourceLoc&); diff --git a/gtests/Hlsl.FromFile.cpp b/gtests/Hlsl.FromFile.cpp index 20fc69058..42d306179 100644 --- a/gtests/Hlsl.FromFile.cpp +++ b/gtests/Hlsl.FromFile.cpp @@ -247,7 +247,7 @@ INSTANTIATE_TEST_CASE_P( {"hlsl.matrixindex.frag", "main"}, {"hlsl.nonstaticMemberFunction.frag", "main"}, {"hlsl.numericsuffixes.frag", "main"}, - {"hlsl.numthreads.comp", "main_aux1"}, + {"hlsl.numthreads.comp", "main_aux2"}, {"hlsl.overload.frag", "PixelShaderFunction"}, {"hlsl.opaque-type-bug.frag", "main"}, {"hlsl.params.default.frag", "main"}, diff --git a/hlsl/hlslAttributes.cpp b/hlsl/hlslAttributes.cpp index 2d204d315..261cec346 100644 --- a/hlsl/hlslAttributes.cpp +++ b/hlsl/hlslAttributes.cpp @@ -34,157 +34,73 @@ // #include "hlslAttributes.h" -#include -#include -#include +#include "hlslParseHelper.h" namespace glslang { // Map the given string to an attribute enum from TAttributeType, // or EatNone if invalid. - TAttributeType TAttributeMap::attributeFromName(const TString& nameSpace, const TString& name) + TAttributeType HlslParseContext::attributeFromName(const TString& nameSpace, const TString& name) const { - // These are case insensitive. - TString lowername(name); - std::transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower); - TString lowernameSpace(nameSpace); - std::transform(lowernameSpace.begin(), lowernameSpace.end(), lowernameSpace.begin(), ::tolower); - // handle names within a namespace - if (lowernameSpace == "vk") { - if (lowername == "input_attachment_index") + if (nameSpace == "vk") { + if (name == "input_attachment_index") return EatInputAttachment; - else if (lowername == "location") + else if (name == "location") return EatLocation; - else if (lowername == "binding") + else if (name == "binding") return EatBinding; - else if (lowername == "global_cbuffer_binding") + else if (name == "global_cbuffer_binding") return EatGlobalBinding; - else if (lowername == "builtin") + else if (name == "builtin") return EatBuiltIn; - else if (lowername == "constant_id") + else if (name == "constant_id") return EatConstantId; - else if (lowername == "push_constant") + else if (name == "push_constant") return EatPushConstant; - } else if (lowernameSpace.size() > 0) + } else if (nameSpace.size() > 0) return EatNone; // handle names with no namespace - if (lowername == "allow_uav_condition") + if (name == "allow_uav_condition") return EatAllow_uav_condition; - else if (lowername == "branch") + else if (name == "branch") return EatBranch; - else if (lowername == "call") + else if (name == "call") return EatCall; - else if (lowername == "domain") + else if (name == "domain") return EatDomain; - else if (lowername == "earlydepthstencil") + else if (name == "earlydepthstencil") return EatEarlyDepthStencil; - else if (lowername == "fastopt") + else if (name == "fastopt") return EatFastOpt; - else if (lowername == "flatten") + else if (name == "flatten") return EatFlatten; - else if (lowername == "forcecase") + else if (name == "forcecase") return EatForceCase; - else if (lowername == "instance") + else if (name == "instance") return EatInstance; - else if (lowername == "maxtessfactor") + else if (name == "maxtessfactor") return EatMaxTessFactor; - else if (lowername == "maxvertexcount") + else if (name == "maxvertexcount") return EatMaxVertexCount; - else if (lowername == "numthreads") + else if (name == "numthreads") return EatNumThreads; - else if (lowername == "outputcontrolpoints") + else if (name == "outputcontrolpoints") return EatOutputControlPoints; - else if (lowername == "outputtopology") + else if (name == "outputtopology") return EatOutputTopology; - else if (lowername == "partitioning") + else if (name == "partitioning") return EatPartitioning; - else if (lowername == "patchconstantfunc") + else if (name == "patchconstantfunc") return EatPatchConstantFunc; - else if (lowername == "unroll") + else if (name == "unroll") return EatUnroll; - else if (lowername == "loop") + else if (name == "loop") return EatLoop; else return EatNone; } - // Look up entry, inserting if it's not there, and if name is a valid attribute name - // as known by attributeFromName. - TAttributeType TAttributeMap::setAttribute(const TString& nameSpace, const TString* name, TIntermAggregate* value) - { - if (name == nullptr) - return EatNone; - - const TAttributeType attr = attributeFromName(nameSpace, *name); - - if (attr != EatNone) - attributes[attr] = value; - - return attr; - } - - // Look up entry (const version), and return aggregate node. This cannot change the map. - const TIntermAggregate* TAttributeMap::operator[](TAttributeType attr) const - { - const auto entry = attributes.find(attr); - - return (entry == attributes.end()) ? nullptr : entry->second; - } - - // True if entry exists in map (even if value is nullptr) - bool TAttributeMap::contains(TAttributeType attr) const - { - return attributes.find(attr) != attributes.end(); - } - - // extract integers out of attribute arguments stored in attribute aggregate - bool TAttributeMap::getInt(TAttributeType attr, int& value, int argNum) const - { - const TConstUnion* intConst = getConstUnion(attr, EbtInt, argNum); - - if (intConst == nullptr) - return false; - - value = intConst->getIConst(); - return true; - }; - - // extract strings out of attribute arguments stored in attribute aggregate. - // convert to lower case if converToLower is true (for case-insensitive compare convenience) - bool TAttributeMap::getString(TAttributeType attr, TString& value, int argNum, bool convertToLower) const - { - const TConstUnion* stringConst = getConstUnion(attr, EbtString, argNum); - - if (stringConst == nullptr) - return false; - - value = *stringConst->getSConst(); - - // Convenience. - if (convertToLower) - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - - return true; - }; - - // Helper to get attribute const union. Returns nullptr on failure. - const TConstUnion* TAttributeMap::getConstUnion(TAttributeType attr, TBasicType basicType, int argNum) const - { - const TIntermAggregate* attrAgg = (*this)[attr]; - if (attrAgg == nullptr) - return nullptr; - - if (argNum >= int(attrAgg->getSequence().size())) - return nullptr; - - const TConstUnion* constVal = &attrAgg->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0]; - if (constVal == nullptr || constVal->getType() != basicType) - return nullptr; - - return constVal; - } - } // end namespace glslang diff --git a/hlsl/hlslAttributes.h b/hlsl/hlslAttributes.h index 2c3cf76e6..b1cc0372e 100644 --- a/hlsl/hlslAttributes.h +++ b/hlsl/hlslAttributes.h @@ -38,93 +38,22 @@ #include #include + +#include "../glslang/MachineIndependent/attribute.h" +#include "../glslang/MachineIndependent/SymbolTable.h" #include "hlslScanContext.h" -#include "../glslang/Include/Common.h" namespace glslang { - enum TAttributeType { - EatNone, - EatAllow_uav_condition, - EatBranch, - EatCall, - EatDomain, - EatEarlyDepthStencil, - EatFastOpt, - EatFlatten, - EatForceCase, - EatInstance, - EatMaxTessFactor, - EatNumThreads, - EatMaxVertexCount, - EatOutputControlPoints, - EatOutputTopology, - EatPartitioning, - EatPatchConstantFunc, - EatPatchSize, - EatUnroll, - EatLoop, - EatBinding, - EatGlobalBinding, - EatLocation, - EatInputAttachment, - EatBuiltIn, - EatPushConstant, - EatConstantId - }; -} - -namespace std { - // Allow use of TAttributeType enum in hash_map without calling code having to cast. - template <> struct hash { - std::size_t operator()(glslang::TAttributeType attr) const { - return std::hash()(int(attr)); - } - }; -} // end namespace std - -namespace glslang { - class TIntermAggregate; - - class TAttributeMap { - public: - int size() const { return (int)attributes.size(); } - - // Search for and potentially add the attribute into the map. Return the - // attribute type enum for it, if found, else EatNone. - TAttributeType setAttribute(const TString& nameSpace, const TString* name, TIntermAggregate* value); - - // Const lookup: search for (but do not modify) the attribute in the map. - const TIntermAggregate* operator[](TAttributeType) const; - - // True if entry exists in map (even if value is nullptr) - bool contains(TAttributeType) const; - - // Obtain attribute as integer - bool getInt(TAttributeType attr, int& value, int argNum = 0) const; - - // Obtain attribute as string, with optional to-lower transform - bool getString(TAttributeType attr, TString& value, int argNum = 0, bool convertToLower = true) const; - - protected: - // Helper to get attribute const union - const TConstUnion* getConstUnion(TAttributeType attr, TBasicType, int argNum) const; - - // Find an attribute enum given its name. - static TAttributeType attributeFromName(const TString& nameSpace, const TString& name); - - std::unordered_map attributes; - }; class TFunctionDeclarator { public: TFunctionDeclarator() : function(nullptr), body(nullptr) { } TSourceLoc loc; TFunction* function; - TAttributeMap attributes; + TAttributes attributes; TVector* body; }; } // end namespace glslang - #endif // HLSLATTRIBUTES_H_ diff --git a/hlsl/hlslGrammar.cpp b/hlsl/hlslGrammar.cpp index 60ea50e6e..57e34d1d1 100755 --- a/hlsl/hlslGrammar.cpp +++ b/hlsl/hlslGrammar.cpp @@ -396,6 +396,9 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) if (peekTokenClass(EHTokLeftParen)) { // looks like function parameters + // merge in the attributes into the return type + parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType, true); + // Potentially rename shader entry point function. No-op most of the time. parseContext.renameShaderFunction(fullName); @@ -423,7 +426,13 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) parseContext.handleFunctionDeclarator(declarator.loc, *declarator.function, true); } } else { - // A variable declaration. Fix the storage qualifier if it's a global. + // A variable declaration. + + // merge in the attributes, the first time around, into the shared type + if (! declarator_list) + parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType); + + // Fix the storage qualifier if it's a global. if (declaredType.getQualifier().storage == EvqTemporary && parseContext.symbolTable.atGlobalLevel()) declaredType.getQualifier().storage = EvqUniform; @@ -536,13 +545,16 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) bool HlslGrammar::acceptControlDeclaration(TIntermNode*& node) { node = nullptr; - TAttributeMap attributes; + TAttributes attributes; // fully_specified_type TType type; if (! acceptFullySpecifiedType(type, attributes)) return false; + if (attributes.size() > 0) + parseContext.warn(token.loc, "attributes don't apply to control declaration", "", ""); + // filter out type casts if (peekTokenClass(EHTokLeftParen)) { recedeToken(); @@ -578,12 +590,12 @@ bool HlslGrammar::acceptControlDeclaration(TIntermNode*& node) // : type_specifier // | type_qualifier type_specifier // -bool HlslGrammar::acceptFullySpecifiedType(TType& type, const TAttributeMap& attributes) +bool HlslGrammar::acceptFullySpecifiedType(TType& type, const TAttributes& attributes) { TIntermNode* nodeList = nullptr; return acceptFullySpecifiedType(type, nodeList, attributes); } -bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList, const TAttributeMap& attributes, bool forbidDeclarators) +bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList, const TAttributes& attributes, bool forbidDeclarators) { // type_qualifier TQualifier qualifier; @@ -608,7 +620,7 @@ bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList, parseContext.mergeQualifiers(type.getQualifier(), qualifier); // merge in the attributes - parseContext.transferTypeAttributes(attributes, type); + parseContext.transferTypeAttributes(token.loc, attributes, type); // further, it can create an anonymous instance of the block // (cbuffer and tbuffer don't consume the next identifier, and @@ -633,9 +645,6 @@ bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList, qualifier.builtIn = type.getQualifier().builtIn; type.getQualifier() = qualifier; - - // merge in the attributes - parseContext.transferTypeAttributes(attributes, type); } return true; @@ -2335,7 +2344,7 @@ bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList, TIntermNode* // struct_declaration // attributes - TAttributeMap attributes; + TAttributes attributes; acceptAttributes(attributes); bool declarator_list = false; @@ -2346,6 +2355,9 @@ bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList, TIntermNode* expected("member type"); return false; } + + // merge in the attributes + parseContext.transferTypeAttributes(token.loc, attributes, memberType); // struct_declarator COMMA struct_declarator ... bool functionDefinitionAccepted = false; @@ -2542,7 +2554,7 @@ bool HlslGrammar::acceptDefaultParameterDeclaration(const TType& type, TIntermTy bool HlslGrammar::acceptParameterDeclaration(TFunction& function) { // attributes - TAttributeMap attributes; + TAttributes attributes; acceptAttributes(attributes); // fully_specified_type @@ -2550,6 +2562,9 @@ bool HlslGrammar::acceptParameterDeclaration(TFunction& function) if (! acceptFullySpecifiedType(*type, attributes)) return false; + // merge in the attributes + parseContext.transferTypeAttributes(token.loc, attributes, *type); + // identifier HlslToken idToken; acceptIdentifier(idToken); @@ -3386,7 +3401,7 @@ bool HlslGrammar::acceptStatement(TIntermNode*& statement) statement = nullptr; // attributes - TAttributeMap attributes; + TAttributes attributes; acceptAttributes(attributes); // attributed_statement @@ -3458,7 +3473,7 @@ bool HlslGrammar::acceptStatement(TIntermNode*& statement) // | PATCHCONSTANTFUNC // | NUMTHREADS LEFT_PAREN x_size, y_size,z z_size RIGHT_PAREN // -void HlslGrammar::acceptAttributes(TAttributeMap& attributes) +void HlslGrammar::acceptAttributes(TAttributes& attributes) { // For now, accept the [ XXX(X) ] syntax, but drop all but // numthreads, which is used to set the CS local size. @@ -3529,9 +3544,16 @@ void HlslGrammar::acceptAttributes(TAttributeMap& attributes) return; } - // Add any values we found into the attribute map. This accepts - // (and ignores) values not mapping to a known TAttributeType; - attributes.setAttribute(nameSpace, attributeToken.string, expressions); + // Add any values we found into the attribute map. + if (attributeToken.string != nullptr) { + TAttributeType attributeType = parseContext.attributeFromName(nameSpace, *attributeToken.string); + if (attributeType == EatNone) + parseContext.warn(attributeToken.loc, "unrecognized attribute", attributeToken.string->c_str(), ""); + else { + TAttributeArgs attributeArgs = { attributeType, expressions }; + attributes.push_back(attributeArgs); + } + } } while (true); } @@ -3539,12 +3561,10 @@ void HlslGrammar::acceptAttributes(TAttributeMap& attributes) // : IF LEFT_PAREN expression RIGHT_PAREN statement // : IF LEFT_PAREN expression RIGHT_PAREN statement ELSE statement // -bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttributeMap& attributes) +bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttributes& attributes) { TSourceLoc loc = token.loc; - const TSelectionControl control = parseContext.handleSelectionControl(attributes); - // IF if (! acceptTokenClass(EHTokIf)) return false; @@ -3582,7 +3602,9 @@ bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttri } // Put the pieces together - statement = intermediate.addSelection(condition, thenElse, loc, control); + statement = intermediate.addSelection(condition, thenElse, loc); + parseContext.handleSelectionAttributes(loc, statement->getAsSelectionNode(), attributes); + parseContext.popScope(); --parseContext.controlFlowNestingLevel; @@ -3592,13 +3614,11 @@ bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttri // switch_statement // : SWITCH LEFT_PAREN expression RIGHT_PAREN compound_statement // -bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttributeMap& attributes) +bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttributes& attributes) { // SWITCH TSourceLoc loc = token.loc; - const TSelectionControl control = parseContext.handleSelectionControl(attributes); - if (! acceptTokenClass(EHTokSwitch)) return false; @@ -3618,7 +3638,8 @@ bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttribut --parseContext.controlFlowNestingLevel; if (statementOkay) - statement = parseContext.addSwitch(loc, switchExpression, statement ? statement->getAsAggregate() : nullptr, control); + statement = parseContext.addSwitch(loc, switchExpression, statement ? statement->getAsAggregate() : nullptr, + attributes); parseContext.popSwitchSequence(); parseContext.popScope(); @@ -3632,7 +3653,7 @@ bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttribut // | FOR LEFT_PAREN for_init_statement for_rest_statement RIGHT_PAREN statement // // Non-speculative, only call if it needs to be found; WHILE or DO or FOR already seen. -bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttributeMap& attributes) +bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttributes& attributes) { TSourceLoc loc = token.loc; TIntermTyped* condition = nullptr; @@ -3642,9 +3663,8 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttri // WHILE or DO or FOR advanceToken(); - - const TLoopControl control = parseContext.handleLoopControl(attributes); + TIntermLoop* loopNode = nullptr; switch (loop) { case EHTokWhile: // so that something declared in the condition is scoped to the lifetime @@ -3670,9 +3690,9 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttri parseContext.popScope(); --parseContext.controlFlowNestingLevel; - statement = intermediate.addLoop(statement, condition, nullptr, true, loc, control); - - return true; + loopNode = intermediate.addLoop(statement, condition, nullptr, true, loc); + statement = loopNode; + break; case EHTokDo: parseContext.nestLooping(); // this only needs to work right if no errors @@ -3703,9 +3723,9 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttri parseContext.unnestLooping(); --parseContext.controlFlowNestingLevel; - statement = intermediate.addLoop(statement, condition, 0, false, loc, control); - - return true; + loopNode = intermediate.addLoop(statement, condition, 0, false, loc); + statement = loopNode; + break; case EHTokFor: { @@ -3747,18 +3767,21 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttri return false; } - statement = intermediate.addForLoop(statement, initNode, condition, iterator, true, loc, control); + statement = intermediate.addForLoop(statement, initNode, condition, iterator, true, loc, loopNode); parseContext.popScope(); parseContext.unnestLooping(); --parseContext.controlFlowNestingLevel; - return true; + break; } default: return false; } + + parseContext.handleLoopAttributes(loc, loopNode, attributes); + return true; } // jump_statement diff --git a/hlsl/hlslGrammar.h b/hlsl/hlslGrammar.h index 9e58bfda2..046f7957e 100755 --- a/hlsl/hlslGrammar.h +++ b/hlsl/hlslGrammar.h @@ -43,7 +43,6 @@ namespace glslang { - class TAttributeMap; class TFunctionDeclarator; // Should just be the grammar aspect of HLSL. @@ -71,8 +70,8 @@ namespace glslang { bool acceptControlDeclaration(TIntermNode*& node); bool acceptSamplerDeclarationDX9(TType&); bool acceptSamplerState(); - bool acceptFullySpecifiedType(TType&, const TAttributeMap&); - bool acceptFullySpecifiedType(TType&, TIntermNode*& nodeList, const TAttributeMap&, bool forbidDeclarators = false); + bool acceptFullySpecifiedType(TType&, const TAttributes&); + bool acceptFullySpecifiedType(TType&, TIntermNode*& nodeList, const TAttributes&, bool forbidDeclarators = false); bool acceptQualifier(TQualifier&); bool acceptLayoutQualifierList(TQualifier&); bool acceptType(TType&); @@ -117,10 +116,10 @@ namespace glslang { bool acceptScopedCompoundStatement(TIntermNode*&); bool acceptStatement(TIntermNode*&); bool acceptNestedStatement(TIntermNode*&); - void acceptAttributes(TAttributeMap&); - bool acceptSelectionStatement(TIntermNode*&, const TAttributeMap&); - bool acceptSwitchStatement(TIntermNode*&, const TAttributeMap&); - bool acceptIterationStatement(TIntermNode*&, const TAttributeMap&); + void acceptAttributes(TAttributes&); + bool acceptSelectionStatement(TIntermNode*&, const TAttributes&); + bool acceptSwitchStatement(TIntermNode*&, const TAttributes&); + bool acceptIterationStatement(TIntermNode*&, const TAttributes&); bool acceptJumpStatement(TIntermNode*&); bool acceptCaseLabel(TIntermNode*&); bool acceptDefaultLabel(TIntermNode*&); diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp index ac72e2700..50e60f021 100755 --- a/hlsl/hlslParseHelper.cpp +++ b/hlsl/hlslParseHelper.cpp @@ -1620,7 +1620,7 @@ void HlslParseContext::addStructBufferHiddenCounterParam(const TSourceLoc& loc, // Returns an aggregate of parameter-symbol nodes. // TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function, - const TAttributeMap& attributes, + const TAttributes& attributes, TIntermNode*& entryPointTree) { currentCaller = function.getMangledName(); @@ -1717,189 +1717,217 @@ TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& l } // Handle all [attrib] attribute for the shader entry point -void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributeMap& attributes) +void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributes& attributes) { - // Handle entry-point function attributes - const TIntermAggregate* numThreads = attributes[EatNumThreads]; - if (numThreads != nullptr) { - const TIntermSequence& sequence = numThreads->getSequence(); - - for (int lid = 0; lid < int(sequence.size()); ++lid) - intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst()); - } - - // MaxVertexCount - if (attributes.contains(EatMaxVertexCount)) { - int maxVertexCount; - - if (! attributes.getInt(EatMaxVertexCount, maxVertexCount)) { - error(loc, "invalid maxvertexcount", "", ""); - } else { - if (! intermediate.setVertices(maxVertexCount)) - error(loc, "cannot change previously set maxvertexcount attribute", "", ""); + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatNumThreads: + { + const TIntermSequence& sequence = it->args->getSequence(); + for (int lid = 0; lid < int(sequence.size()); ++lid) + intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst()); + break; } - } + case EatMaxVertexCount: + { + int maxVertexCount; - // Handle [patchconstantfunction("...")] - if (attributes.contains(EatPatchConstantFunc)) { - TString pcfName; - if (! attributes.getString(EatPatchConstantFunc, pcfName, 0, false)) { - error(loc, "invalid patch constant function", "", ""); - } else { - patchConstantFunctionName = pcfName; + if (! it->getInt(maxVertexCount)) { + error(loc, "invalid maxvertexcount", "", ""); + } else { + if (! intermediate.setVertices(maxVertexCount)) + error(loc, "cannot change previously set maxvertexcount attribute", "", ""); + } + break; } - } - - // Handle [domain("...")] - if (attributes.contains(EatDomain)) { - TString domainStr; - if (! attributes.getString(EatDomain, domainStr)) { - error(loc, "invalid domain", "", ""); - } else { - TLayoutGeometry domain = ElgNone; - - if (domainStr == "tri") { - domain = ElgTriangles; - } else if (domainStr == "quad") { - domain = ElgQuads; - } else if (domainStr == "isoline") { - domain = ElgIsolines; + case EatPatchConstantFunc: + { + TString pcfName; + if (! it->getString(pcfName, 0, false)) { + error(loc, "invalid patch constant function", "", ""); } else { - error(loc, "unsupported domain type", domainStr.c_str(), ""); - } - - if (language == EShLangTessEvaluation) { - if (! intermediate.setInputPrimitive(domain)) - error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), ""); - } else { - if (! intermediate.setOutputPrimitive(domain)) - error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), ""); + patchConstantFunctionName = pcfName; } + break; } - } - - // Handle [outputtopology("...")] - if (attributes.contains(EatOutputTopology)) { - TString topologyStr; - if (! attributes.getString(EatOutputTopology, topologyStr)) { - error(loc, "invalid outputtopology", "", ""); - } else { - TVertexOrder vertexOrder = EvoNone; - TLayoutGeometry primitive = ElgNone; - - if (topologyStr == "point") { - intermediate.setPointMode(); - } else if (topologyStr == "line") { - primitive = ElgIsolines; - } else if (topologyStr == "triangle_cw") { - vertexOrder = EvoCw; - primitive = ElgTriangles; - } else if (topologyStr == "triangle_ccw") { - vertexOrder = EvoCcw; - primitive = ElgTriangles; + case EatDomain: + { + // Handle [domain("...")] + TString domainStr; + if (! it->getString(domainStr)) { + error(loc, "invalid domain", "", ""); } else { - error(loc, "unsupported outputtopology type", topologyStr.c_str(), ""); - } + TLayoutGeometry domain = ElgNone; - if (vertexOrder != EvoNone) { - if (! intermediate.setVertexOrder(vertexOrder)) { - error(loc, "cannot change previously set outputtopology", - TQualifier::getVertexOrderString(vertexOrder), ""); + if (domainStr == "tri") { + domain = ElgTriangles; + } else if (domainStr == "quad") { + domain = ElgQuads; + } else if (domainStr == "isoline") { + domain = ElgIsolines; + } else { + error(loc, "unsupported domain type", domainStr.c_str(), ""); + } + + if (language == EShLangTessEvaluation) { + if (! intermediate.setInputPrimitive(domain)) + error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), ""); + } else { + if (! intermediate.setOutputPrimitive(domain)) + error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), ""); } } - if (primitive != ElgNone) - intermediate.setOutputPrimitive(primitive); + break; } - } - - // Handle [partitioning("...")] - if (attributes.contains(EatPartitioning)) { - TString partitionStr; - if (! attributes.getString(EatPartitioning, partitionStr)) { - error(loc, "invalid partitioning", "", ""); - } else { - TVertexSpacing partitioning = EvsNone; - - if (partitionStr == "integer") { - partitioning = EvsEqual; - } else if (partitionStr == "fractional_even") { - partitioning = EvsFractionalEven; - } else if (partitionStr == "fractional_odd") { - partitioning = EvsFractionalOdd; - //} else if (partition == "pow2") { // TODO: currently nothing to map this to. + case EatOutputTopology: + { + // Handle [outputtopology("...")] + TString topologyStr; + if (! it->getString(topologyStr)) { + error(loc, "invalid outputtopology", "", ""); } else { - error(loc, "unsupported partitioning type", partitionStr.c_str(), ""); - } + TVertexOrder vertexOrder = EvoNone; + TLayoutGeometry primitive = ElgNone; - if (! intermediate.setVertexSpacing(partitioning)) - error(loc, "cannot change previously set partitioning", - TQualifier::getVertexSpacingString(partitioning), ""); + if (topologyStr == "point") { + intermediate.setPointMode(); + } else if (topologyStr == "line") { + primitive = ElgIsolines; + } else if (topologyStr == "triangle_cw") { + vertexOrder = EvoCw; + primitive = ElgTriangles; + } else if (topologyStr == "triangle_ccw") { + vertexOrder = EvoCcw; + primitive = ElgTriangles; + } else { + error(loc, "unsupported outputtopology type", topologyStr.c_str(), ""); + } + + if (vertexOrder != EvoNone) { + if (! intermediate.setVertexOrder(vertexOrder)) { + error(loc, "cannot change previously set outputtopology", + TQualifier::getVertexOrderString(vertexOrder), ""); + } + } + if (primitive != ElgNone) + intermediate.setOutputPrimitive(primitive); + } + break; } - } + case EatPartitioning: + { + // Handle [partitioning("...")] + TString partitionStr; + if (! it->getString(partitionStr)) { + error(loc, "invalid partitioning", "", ""); + } else { + TVertexSpacing partitioning = EvsNone; + + if (partitionStr == "integer") { + partitioning = EvsEqual; + } else if (partitionStr == "fractional_even") { + partitioning = EvsFractionalEven; + } else if (partitionStr == "fractional_odd") { + partitioning = EvsFractionalOdd; + //} else if (partition == "pow2") { // TODO: currently nothing to map this to. + } else { + error(loc, "unsupported partitioning type", partitionStr.c_str(), ""); + } - // Handle [outputcontrolpoints("...")] - if (attributes.contains(EatOutputControlPoints)) { - int ctrlPoints; - if (! attributes.getInt(EatOutputControlPoints, ctrlPoints)) { - error(loc, "invalid outputcontrolpoints", "", ""); - } else { - if (! intermediate.setVertices(ctrlPoints)) { - error(loc, "cannot change previously set outputcontrolpoints attribute", "", ""); + if (! intermediate.setVertexSpacing(partitioning)) + error(loc, "cannot change previously set partitioning", + TQualifier::getVertexSpacingString(partitioning), ""); } + break; + } + case EatOutputControlPoints: + { + // Handle [outputcontrolpoints("...")] + int ctrlPoints; + if (! it->getInt(ctrlPoints)) { + error(loc, "invalid outputcontrolpoints", "", ""); + } else { + if (! intermediate.setVertices(ctrlPoints)) { + error(loc, "cannot change previously set outputcontrolpoints attribute", "", ""); + } + } + break; + } + case EatBuiltIn: + case EatLocation: + // tolerate these because of dual use of entrypoint and type attributes + break; + default: + warn(loc, "attribute does not apply to entry point", "", ""); + break; } } } // Update the given type with any type-like attribute information in the // attributes. -void HlslParseContext::transferTypeAttributes(const TAttributeMap& attributes, TType& type) +void HlslParseContext::transferTypeAttributes(const TSourceLoc& loc, const TAttributes& attributes, TType& type, + bool allowEntry) { if (attributes.size() == 0) return; - // location int value; - if (attributes.getInt(EatLocation, value)) - type.getQualifier().layoutLocation = value; - - // binding - if (attributes.getInt(EatBinding, value)) { - type.getQualifier().layoutBinding = value; - type.getQualifier().layoutSet = 0; - } - - // set - if (attributes.getInt(EatBinding, value, 1)) - type.getQualifier().layoutSet = value; - - // global cbuffer binding - if (attributes.getInt(EatGlobalBinding, value)) - globalUniformBinding = value; - - // global cbuffer binding - if (attributes.getInt(EatGlobalBinding, value, 1)) - globalUniformSet = value; - - // input attachment - if (attributes.getInt(EatInputAttachment, value)) - type.getQualifier().layoutAttachment = value; - - // PointSize built-in TString builtInString; - if (attributes.getString(EatBuiltIn, builtInString, 0, false)) { - if (builtInString == "PointSize") - type.getQualifier().builtIn = EbvPointSize; - } - - // push_constant - if (attributes.contains(EatPushConstant)) - type.getQualifier().layoutPushConstant = true; - - // specialization constant - if (attributes.getInt(EatConstantId, value)) { - TSourceLoc loc; - loc.init(); - setSpecConstantId(loc, type.getQualifier(), value); + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatLocation: + // location + if (it->getInt(value)) + type.getQualifier().layoutLocation = value; + break; + case EatBinding: + // binding + if (it->getInt(value)) { + type.getQualifier().layoutBinding = value; + type.getQualifier().layoutSet = 0; + } + // set + if (it->getInt(value, 1)) + type.getQualifier().layoutSet = value; + break; + case EatGlobalBinding: + // global cbuffer binding + if (it->getInt(value)) + globalUniformBinding = value; + // global cbuffer binding + if (it->getInt(value, 1)) + globalUniformSet = value; + break; + case EatInputAttachment: + // input attachment + if (it->getInt(value)) + type.getQualifier().layoutAttachment = value; + break; + case EatBuiltIn: + // PointSize built-in + if (it->getString(builtInString, 0, false)) { + if (builtInString == "PointSize") + type.getQualifier().builtIn = EbvPointSize; + } + break; + case EatPushConstant: + // push_constant + type.getQualifier().layoutPushConstant = true; + break; + case EatConstantId: + // specialization constant + if (it->getInt(value)) { + TSourceLoc loc; + loc.init(); + setSpecConstantId(loc, type.getQualifier(), value); + } + break; + default: + if (! allowEntry) + warn(loc, "attribute does not apply to a type", "", ""); + break; + } } } @@ -1936,7 +1964,7 @@ void HlslParseContext::transferTypeAttributes(const TAttributeMap& attributes, T // a subtree that creates the entry point. // TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction, - const TAttributeMap& attributes) + const TAttributes& attributes) { // Return true if this is a tessellation patch constant function input to a domain shader. const auto isDsPcfInput = [this](const TType& type) { @@ -8792,29 +8820,75 @@ bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayout } // -// Selection hints +// Selection attributes // -TSelectionControl HlslParseContext::handleSelectionControl(const TAttributeMap& attributes) const +void HlslParseContext::handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection* selection, + const TAttributes& attributes) { - if (attributes.contains(EatFlatten)) - return ESelectionControlFlatten; - else if (attributes.contains(EatBranch)) - return ESelectionControlDontFlatten; - else - return ESelectionControlNone; + if (selection == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatFlatten: + selection->setFlatten(); + break; + case EatBranch: + selection->setDontFlatten(); + break; + default: + warn(loc, "attribute does not apply to a selection", "", ""); + break; + } + } +} + +// +// Switch attributes +// +void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch* selection, + const TAttributes& attributes) +{ + if (selection == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatFlatten: + selection->setFlatten(); + break; + case EatBranch: + selection->setDontFlatten(); + break; + default: + warn(loc, "attribute does not apply to a switch", "", ""); + break; + } + } } // // Loop hints // -TLoopControl HlslParseContext::handleLoopControl(const TAttributeMap& attributes) const +void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop, + const TAttributes& attributes) { - if (attributes.contains(EatUnroll)) - return ELoopControlUnroll; - else if (attributes.contains(EatLoop)) - return ELoopControlDontUnroll; - else - return ELoopControlNone; + if (loop == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatUnroll: + loop->setUnroll(); + break; + case EatLoop: + loop->setDontUnroll(); + break; + default: + warn(loc, "attribute does not apply to a loop", "", ""); + break; + } + } } // @@ -8959,7 +9033,7 @@ void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIn // into a switch node. // TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, - TIntermAggregate* lastStatements, TSelectionControl control) + TIntermAggregate* lastStatements, const TAttributes& attributes) { wrapupSwitchSubsequence(lastStatements, nullptr); @@ -8986,7 +9060,7 @@ TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* ex TIntermSwitch* switchNode = new TIntermSwitch(expression, body); switchNode->setLoc(loc); - switchNode->setSelectionControl(control); + handleSwitchAttributes(loc, switchNode, attributes); return switchNode; } diff --git a/hlsl/hlslParseHelper.h b/hlsl/hlslParseHelper.h index 833eb8e5b..d85bdfa22 100755 --- a/hlsl/hlslParseHelper.h +++ b/hlsl/hlslParseHelper.h @@ -38,12 +38,12 @@ #include "../glslang/MachineIndependent/parseVersions.h" #include "../glslang/MachineIndependent/ParseHelper.h" +#include "../glslang/MachineIndependent/attribute.h" #include namespace glslang { -class TAttributeMap; // forward declare class TFunctionDeclarator; class HlslParseContext : public TParseContextBase { @@ -80,10 +80,10 @@ public: bool isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field); void assignToInterface(TVariable& variable); void handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype); - TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&, const TAttributeMap&, TIntermNode*& entryPointTree); - TIntermNode* transformEntryPoint(const TSourceLoc&, TFunction&, const TAttributeMap&); - void handleEntryPointAttributes(const TSourceLoc&, const TAttributeMap&); - void transferTypeAttributes(const TAttributeMap&, TType&); + TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&, const TAttributes&, TIntermNode*& entryPointTree); + TIntermNode* transformEntryPoint(const TSourceLoc&, TFunction&, const TAttributes&); + void handleEntryPointAttributes(const TSourceLoc&, const TAttributes&); + void transferTypeAttributes(const TSourceLoc&, const TAttributes&, TType&, bool allowEntry = false); void handleFunctionBody(const TSourceLoc&, TFunction&, TIntermNode* functionBody, TIntermNode*& node); void remapEntryPointIO(TFunction& function, TVariable*& returnValue, TVector& inputs, TVector& outputs); void remapNonEntryPointIO(TFunction& function); @@ -163,7 +163,7 @@ public: void addQualifierToExisting(const TSourceLoc&, TQualifier, TIdentifierList&); void updateStandaloneQualifierDefaults(const TSourceLoc&, const TPublicType&); void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode); - TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body, TSelectionControl control); + TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body, const TAttributes&); void updateImplicitArraySize(const TSourceLoc&, TIntermNode*, int index); @@ -203,10 +203,11 @@ public: bool handleInputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry); // Determine selection control from attributes - TSelectionControl handleSelectionControl(const TAttributeMap& attributes) const; + void handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection*, const TAttributes& attributes); + void handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch*, const TAttributes& attributes); // Determine loop control from attributes - TLoopControl handleLoopControl(const TAttributeMap& attributes) const; + void handleLoopAttributes(const TSourceLoc& loc, TIntermLoop*, const TAttributes& attributes); // Share struct buffer deep types void shareStructBufferType(TType&); @@ -217,6 +218,8 @@ public: // Obtain the sampler return type of the given sampler in retType. void getTextureReturnType(const TSampler& sampler, TType& retType) const; + TAttributeType attributeFromName(const TString& nameSpace, const TString& name) const; + protected: struct TFlattenData { TFlattenData() : nextBinding(TQualifier::layoutBindingEnd),