From f1709e71460d1165b479b44cf9955999f51a42b5 Mon Sep 17 00:00:00 2001 From: steve-lunarg Date: Tue, 2 May 2017 20:14:50 -0600 Subject: [PATCH] HLSL: implement [unroll] and [loop] attributes This adds infrastructure suitable for any front end to create SPIR-V loop control flags. The only current front end doing so is HLSL. [unroll] turns into spv::LoopControlUnrollMask [loop] turns into spv::LoopControlDontUnrollMask no specification means spv::LoopControlMaskNone --- SPIRV/GlslangToSpv.cpp | 21 +- .../hlsl.attribute.expression.comp.out | 2 +- Test/baseResults/hlsl.doLoop.frag.out | 4 +- Test/baseResults/hlsl.forLoop.frag.out | 2 +- Test/baseResults/hlsl.loopattr.frag.out | 233 ++++++++++++++++++ Test/baseResults/hlsl.whileLoop.frag.out | 2 +- Test/hlsl.loopattr.frag | 14 ++ glslang/Include/intermediate.h | 19 +- glslang/MachineIndependent/Intermediate.cpp | 6 +- .../MachineIndependent/localintermediate.h | 4 +- gtests/Hlsl.FromFile.cpp | 1 + hlsl/hlslAttributes.cpp | 8 + hlsl/hlslAttributes.h | 4 + hlsl/hlslGrammar.cpp | 12 +- hlsl/hlslGrammar.h | 2 +- hlsl/hlslParseHelper.cpp | 14 ++ hlsl/hlslParseHelper.h | 3 + 17 files changed, 334 insertions(+), 17 deletions(-) create mode 100644 Test/baseResults/hlsl.loopattr.frag.out create mode 100644 Test/hlsl.loopattr.frag diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 9e5676d7..cb48d349 100755 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -122,6 +122,7 @@ protected: spv::Decoration TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier); spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable, bool memberDeclaration); spv::ImageFormat TranslateImageFormat(const glslang::TType& type); + spv::LoopControlMask TranslateLoopControl(glslang::TLoopControl) const; spv::Id createSpvVariable(const glslang::TIntermSymbol*); spv::Id getSampledType(const glslang::TSampler&); spv::Id getInvertedSwizzleType(const glslang::TIntermTyped&); @@ -767,6 +768,18 @@ spv::ImageFormat TGlslangToSpvTraverser::TranslateImageFormat(const glslang::TTy } } +spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(glslang::TLoopControl loopControl) 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; + } +} + // Return whether or not the given type is something that should be tied to a // descriptor set. bool IsDescriptorResource(const glslang::TType& type) @@ -1960,6 +1973,12 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn { auto blocks = builder.makeNewLoop(); builder.createBranch(&blocks.head); + + // Loop control: + const spv::LoopControlMask control = TranslateLoopControl(node->getLoopControl()); + + // TODO: dependency length + // Spec requires back edges to target header blocks, and every header block // must dominate its merge block. Make a header block first to ensure these // conditions are met. By definition, it will contain OpLoopMerge, followed @@ -1967,7 +1986,7 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn // instructions in it, since the body/test may have arbitrary instructions, // including merges of its own. builder.setBuildPoint(&blocks.head); - builder.createLoopMerge(&blocks.merge, &blocks.continue_target, spv::LoopControlMaskNone); + builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control); if (node->testFirst() && node->getTest()) { spv::Block& test = builder.makeNewBlock(); builder.createBranch(&test); diff --git a/Test/baseResults/hlsl.attribute.expression.comp.out b/Test/baseResults/hlsl.attribute.expression.comp.out index ed50b9c5..701511e8 100644 --- a/Test/baseResults/hlsl.attribute.expression.comp.out +++ b/Test/baseResults/hlsl.attribute.expression.comp.out @@ -132,7 +132,7 @@ local_size = (4, 6, 8) Store 13(x) 14 Branch 15 15: Label - LoopMerge 17 18 None + LoopMerge 17 18 Unroll Branch 19 19: Label 20: 11(int) Load 13(x) diff --git a/Test/baseResults/hlsl.doLoop.frag.out b/Test/baseResults/hlsl.doLoop.frag.out index f2f36c73..bdd93387 100755 --- a/Test/baseResults/hlsl.doLoop.frag.out +++ b/Test/baseResults/hlsl.doLoop.frag.out @@ -194,7 +194,7 @@ gl_FragCoord origin is upper left 12: Label Branch 13 13: Label - LoopMerge 15 16 None + LoopMerge 15 16 Unroll Branch 14 14: Label Branch 16 @@ -203,7 +203,7 @@ gl_FragCoord origin is upper left 15: Label Branch 19 19: Label - LoopMerge 21 22 None + LoopMerge 21 22 Unroll Branch 20 20: Label Branch 22 diff --git a/Test/baseResults/hlsl.forLoop.frag.out b/Test/baseResults/hlsl.forLoop.frag.out index de1f1c0b..e9908db3 100755 --- a/Test/baseResults/hlsl.forLoop.frag.out +++ b/Test/baseResults/hlsl.forLoop.frag.out @@ -338,7 +338,7 @@ gl_FragCoord origin is upper left 23: Label Branch 25 25: Label - LoopMerge 27 28 None + LoopMerge 27 28 Unroll Branch 29 29: Label 30: 7(fvec4) Load 10(input) diff --git a/Test/baseResults/hlsl.loopattr.frag.out b/Test/baseResults/hlsl.loopattr.frag.out new file mode 100644 index 00000000..81b6c294 --- /dev/null +++ b/Test/baseResults/hlsl.loopattr.frag.out @@ -0,0 +1,233 @@ +hlsl.loopattr.frag +Shader version: 500 +gl_FragCoord origin is upper left +0:? Sequence +0:3 Function Definition: @main( ( temp 4-component vector of float) +0:3 Function Parameters: +0:? Sequence +0:5 Sequence +0:5 move second child to first child ( temp int) +0:5 'x' ( temp int) +0:5 Constant: +0:5 0 (const int) +0:5 Loop with condition tested first +0:5 Loop Condition +0:5 Compare Less Than ( temp bool) +0:5 'x' ( temp int) +0:5 Constant: +0:5 5 (const int) +0:5 No loop body +0:5 Loop Terminal Expression +0:5 Pre-Increment ( temp int) +0:5 'x' ( temp int) +0:8 Sequence +0:8 move second child to first child ( temp int) +0:8 'y' ( temp int) +0:8 Constant: +0:8 0 (const int) +0:8 Loop with condition tested first +0:8 Loop Condition +0:8 Compare Less Than ( temp bool) +0:8 'y' ( temp int) +0:8 Constant: +0:8 5 (const int) +0:8 No loop body +0:8 Loop Terminal Expression +0:8 Pre-Increment ( temp int) +0:8 'y' ( temp int) +0:11 Sequence +0:11 move second child to first child ( temp int) +0:11 'z' ( temp int) +0:11 Constant: +0:11 0 (const int) +0:11 Loop with condition tested first +0:11 Loop Condition +0:11 Compare Less Than ( temp bool) +0:11 'z' ( temp int) +0:11 Constant: +0:11 5 (const int) +0:11 No loop body +0:11 Loop Terminal Expression +0:11 Pre-Increment ( temp int) +0:11 'z' ( temp int) +0:13 Branch: Return with expression +0:13 Constant: +0:13 0.000000 +0:13 0.000000 +0:13 0.000000 +0:13 0.000000 +0:3 Function Definition: main( ( temp void) +0:3 Function Parameters: +0:? Sequence +0:3 move second child to first child ( temp 4-component vector of float) +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) +0:3 Function Call: @main( ( temp 4-component vector of float) +0:? Linker Objects +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) + + +Linked fragment stage: + + +Shader version: 500 +gl_FragCoord origin is upper left +0:? Sequence +0:3 Function Definition: @main( ( temp 4-component vector of float) +0:3 Function Parameters: +0:? Sequence +0:5 Sequence +0:5 move second child to first child ( temp int) +0:5 'x' ( temp int) +0:5 Constant: +0:5 0 (const int) +0:5 Loop with condition tested first +0:5 Loop Condition +0:5 Compare Less Than ( temp bool) +0:5 'x' ( temp int) +0:5 Constant: +0:5 5 (const int) +0:5 No loop body +0:5 Loop Terminal Expression +0:5 Pre-Increment ( temp int) +0:5 'x' ( temp int) +0:8 Sequence +0:8 move second child to first child ( temp int) +0:8 'y' ( temp int) +0:8 Constant: +0:8 0 (const int) +0:8 Loop with condition tested first +0:8 Loop Condition +0:8 Compare Less Than ( temp bool) +0:8 'y' ( temp int) +0:8 Constant: +0:8 5 (const int) +0:8 No loop body +0:8 Loop Terminal Expression +0:8 Pre-Increment ( temp int) +0:8 'y' ( temp int) +0:11 Sequence +0:11 move second child to first child ( temp int) +0:11 'z' ( temp int) +0:11 Constant: +0:11 0 (const int) +0:11 Loop with condition tested first +0:11 Loop Condition +0:11 Compare Less Than ( temp bool) +0:11 'z' ( temp int) +0:11 Constant: +0:11 5 (const int) +0:11 No loop body +0:11 Loop Terminal Expression +0:11 Pre-Increment ( temp int) +0:11 'z' ( temp int) +0:13 Branch: Return with expression +0:13 Constant: +0:13 0.000000 +0:13 0.000000 +0:13 0.000000 +0:13 0.000000 +0:3 Function Definition: main( ( temp void) +0:3 Function Parameters: +0:? Sequence +0:3 move second child to first child ( temp 4-component vector of float) +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) +0:3 Function Call: @main( ( temp 4-component vector of float) +0:? Linker Objects +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 54 + + Capability Shader + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 4 "main" 52 + ExecutionMode 4 OriginUpperLeft + Source HLSL 500 + Name 4 "main" + Name 9 "@main(" + Name 13 "x" + Name 27 "y" + Name 37 "z" + Name 52 "@entryPointOutput" + Decorate 52(@entryPointOutput) Location 0 + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypeVector 6(float) 4 + 8: TypeFunction 7(fvec4) + 11: TypeInt 32 1 + 12: TypePointer Function 11(int) + 14: 11(int) Constant 0 + 21: 11(int) Constant 5 + 22: TypeBool + 25: 11(int) Constant 1 + 47: 6(float) Constant 0 + 48: 7(fvec4) ConstantComposite 47 47 47 47 + 51: TypePointer Output 7(fvec4) +52(@entryPointOutput): 51(ptr) Variable Output + 4(main): 2 Function None 3 + 5: Label + 53: 7(fvec4) FunctionCall 9(@main() + Store 52(@entryPointOutput) 53 + Return + FunctionEnd + 9(@main(): 7(fvec4) Function None 8 + 10: Label + 13(x): 12(ptr) Variable Function + 27(y): 12(ptr) Variable Function + 37(z): 12(ptr) Variable Function + Store 13(x) 14 + Branch 15 + 15: Label + LoopMerge 17 18 Unroll + Branch 19 + 19: Label + 20: 11(int) Load 13(x) + 23: 22(bool) SLessThan 20 21 + BranchConditional 23 16 17 + 16: Label + Branch 18 + 18: Label + 24: 11(int) Load 13(x) + 26: 11(int) IAdd 24 25 + Store 13(x) 26 + Branch 15 + 17: Label + Store 27(y) 14 + Branch 28 + 28: Label + LoopMerge 30 31 DontUnroll + Branch 32 + 32: Label + 33: 11(int) Load 27(y) + 34: 22(bool) SLessThan 33 21 + BranchConditional 34 29 30 + 29: Label + Branch 31 + 31: Label + 35: 11(int) Load 27(y) + 36: 11(int) IAdd 35 25 + Store 27(y) 36 + Branch 28 + 30: Label + Store 37(z) 14 + Branch 38 + 38: Label + LoopMerge 40 41 None + Branch 42 + 42: Label + 43: 11(int) Load 37(z) + 44: 22(bool) SLessThan 43 21 + BranchConditional 44 39 40 + 39: Label + Branch 41 + 41: Label + 45: 11(int) Load 37(z) + 46: 11(int) IAdd 45 25 + Store 37(z) 46 + Branch 38 + 40: Label + ReturnValue 48 + FunctionEnd diff --git a/Test/baseResults/hlsl.whileLoop.frag.out b/Test/baseResults/hlsl.whileLoop.frag.out index cd47dc7a..3e9b794e 100755 --- a/Test/baseResults/hlsl.whileLoop.frag.out +++ b/Test/baseResults/hlsl.whileLoop.frag.out @@ -171,7 +171,7 @@ gl_FragCoord origin is upper left 28: Label Branch 32 32: Label - LoopMerge 34 35 None + LoopMerge 34 35 Unroll Branch 36 36: Label BranchConditional 31 33 34 diff --git a/Test/hlsl.loopattr.frag b/Test/hlsl.loopattr.frag new file mode 100644 index 00000000..5b4d374f --- /dev/null +++ b/Test/hlsl.loopattr.frag @@ -0,0 +1,14 @@ + +float4 main() : SV_Target0 +{ + // Unroll hint + [unroll(5) ] for (int x=0; x<5; ++x); + + // Don't unroll hint + [loop] for (int y=0; y<5; ++y); + + // No hint + for (int z=0; z<5; ++z); + + return 0; +} diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h index 60b0883b..d9b6d05a 100644 --- a/glslang/Include/intermediate.h +++ b/glslang/Include/intermediate.h @@ -758,6 +758,15 @@ protected: TType type; }; +// +// Loop control hints +// +enum TLoopControl { + ELoopControlNone, + ELoopControlUnroll, + ELoopControlDontUnroll, +}; + // // Handle for, do-while, and while loops. // @@ -767,17 +776,25 @@ public: body(aBody), test(aTest), terminal(aTerminal), - first(testFirst) { } + first(testFirst), + control(ELoopControlNone) + { } + 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; } + 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 }; // diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp index 2c54c5eb..fe0651aa 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -1630,10 +1630,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) +TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TLoopControl control) { TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst); node->setLoc(loc); + node->setLoopControl(control); return node; } @@ -1641,10 +1642,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) +TIntermAggregate* TIntermediate::addForLoop(TIntermNode* body, TIntermNode* initializer, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TLoopControl control) { TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst); node->setLoc(loc); + node->setLoopControl(control); // make a sequence of the initializer and statement TIntermAggregate* loopSequence = makeAggregate(initializer, loc); diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index 2fd2e9fd..29d1bba1 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -283,8 +283,8 @@ 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&); - TIntermAggregate* addForLoop(TIntermNode*, TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&); + TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&, TLoopControl = ELoopControlNone); + TIntermAggregate* addForLoop(TIntermNode*, TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&, TLoopControl = ELoopControlNone); 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 6129ddee..3352ff25 100644 --- a/gtests/Hlsl.FromFile.cpp +++ b/gtests/Hlsl.FromFile.cpp @@ -175,6 +175,7 @@ INSTANTIATE_TEST_CASE_P( {"hlsl.logical.binary.vec.frag", "main"}, {"hlsl.logicalConvert.frag", "main"}, {"hlsl.logical.unary.frag", "main"}, + {"hlsl.loopattr.frag", "main"}, {"hlsl.namespace.frag", "main"}, {"hlsl.nonint-index.frag", "main"}, {"hlsl.matNx1.frag", "main"}, diff --git a/hlsl/hlslAttributes.cpp b/hlsl/hlslAttributes.cpp index df370468..14f7a7bd 100644 --- a/hlsl/hlslAttributes.cpp +++ b/hlsl/hlslAttributes.cpp @@ -80,6 +80,8 @@ namespace glslang { return EatPatchConstantFunc; else if (lowername == "unroll") return EatUnroll; + else if (lowername == "loop") + return EatLoop; else return EatNone; } @@ -107,4 +109,10 @@ namespace glslang { 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(); + } + } // end namespace glslang diff --git a/hlsl/hlslAttributes.h b/hlsl/hlslAttributes.h index ad44d2ab..b32a53cb 100644 --- a/hlsl/hlslAttributes.h +++ b/hlsl/hlslAttributes.h @@ -62,6 +62,7 @@ namespace glslang { EatPatchConstantFunc, EatPatchSize, EatUnroll, + EatLoop, }; } @@ -86,6 +87,9 @@ namespace glslang { // 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; + protected: // Find an attribute enum given its name. static TAttributeType attributeFromName(const TString&); diff --git a/hlsl/hlslGrammar.cpp b/hlsl/hlslGrammar.cpp index a9743c21..cdf8a077 100755 --- a/hlsl/hlslGrammar.cpp +++ b/hlsl/hlslGrammar.cpp @@ -3127,7 +3127,7 @@ bool HlslGrammar::acceptStatement(TIntermNode*& statement) case EHTokFor: case EHTokDo: case EHTokWhile: - return acceptIterationStatement(statement); + return acceptIterationStatement(statement, attributes); case EHTokContinue: case EHTokBreak: @@ -3336,7 +3336,7 @@ bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement) // | 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) +bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttributeMap& attributes) { TSourceLoc loc = token.loc; TIntermTyped* condition = nullptr; @@ -3346,6 +3346,8 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement) // WHILE or DO or FOR advanceToken(); + + const TLoopControl control = parseContext.handleLoopControl(attributes); switch (loop) { case EHTokWhile: @@ -3370,7 +3372,7 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement) parseContext.unnestLooping(); parseContext.popScope(); - statement = intermediate.addLoop(statement, condition, nullptr, true, loc); + statement = intermediate.addLoop(statement, condition, nullptr, true, loc, control); return true; @@ -3402,7 +3404,7 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement) parseContext.unnestLooping(); - statement = intermediate.addLoop(statement, condition, 0, false, loc); + statement = intermediate.addLoop(statement, condition, 0, false, loc, control); return true; @@ -3451,7 +3453,7 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement) return false; } - statement = intermediate.addForLoop(statement, initNode, condition, iterator, true, loc); + statement = intermediate.addForLoop(statement, initNode, condition, iterator, true, loc, control); parseContext.popScope(); parseContext.unnestLooping(); diff --git a/hlsl/hlslGrammar.h b/hlsl/hlslGrammar.h index f1ff1c68..07c88787 100755 --- a/hlsl/hlslGrammar.h +++ b/hlsl/hlslGrammar.h @@ -116,7 +116,7 @@ namespace glslang { void acceptAttributes(TAttributeMap&); bool acceptSelectionStatement(TIntermNode*&); bool acceptSwitchStatement(TIntermNode*&); - bool acceptIterationStatement(TIntermNode*&); + bool acceptIterationStatement(TIntermNode*&, const TAttributeMap&); bool acceptJumpStatement(TIntermNode*&); bool acceptCaseLabel(TIntermNode*&); bool acceptDefaultLabel(TIntermNode*&); diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp index 6d963771..1ee0be87 100755 --- a/hlsl/hlslParseHelper.cpp +++ b/hlsl/hlslParseHelper.cpp @@ -7568,6 +7568,20 @@ bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayout return true; } +// +// Loop hints +// +TLoopControl HlslParseContext::handleLoopControl(const TAttributeMap& attributes) const +{ + if (attributes.contains(EatUnroll)) + return ELoopControlUnroll; + else if (attributes.contains(EatLoop)) + return ELoopControlDontUnroll; + else + return ELoopControlNone; +} + + // // Updating default qualifier for the case of a declaration with just a qualifier, // no type, block, or identifier. diff --git a/hlsl/hlslParseHelper.h b/hlsl/hlslParseHelper.h index cecfcf1f..d5cbc063 100755 --- a/hlsl/hlslParseHelper.h +++ b/hlsl/hlslParseHelper.h @@ -192,6 +192,9 @@ public: bool handleOutputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry); bool handleInputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry); + // Determine loop control from attributes + TLoopControl handleLoopControl(const TAttributeMap& attributes) const; + // Potentially rename shader entry point function void renameShaderFunction(const TString*& name) const;