From 1f4d04687b07649129fc2d09be455a48cb97b47d Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Sat, 12 Jan 2019 17:31:41 +0700 Subject: [PATCH] SPV 1.4: Implement the 5 new loop controls. --- SPIRV/GlslangToSpv.cpp | 34 +++++- SPIRV/SpvBuilder.cpp | 6 +- SPIRV/SpvBuilder.h | 2 +- SPIRV/doc.cpp | 15 ++- Test/baseResults/460.frag.out | 2 +- Test/baseResults/spv.1.4.LoopControl.frag.out | 110 ++++++++++++++++++ .../spv.controlFlowAttributes.frag.out | 6 +- Test/spv.1.4.LoopControl.frag | 19 +++ glslang/Include/intermediate.h | 36 +++++- glslang/MachineIndependent/attribute.cpp | 109 ++++++++++++++--- glslang/MachineIndependent/attribute.h | 7 +- gtests/Spv.FromFile.cpp | 1 + 12 files changed, 312 insertions(+), 35 deletions(-) create mode 100644 Test/baseResults/spv.1.4.LoopControl.frag.out create mode 100644 Test/spv.1.4.LoopControl.frag diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index d25ccd36e..3d0f0c7ac 100644 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -135,7 +135,7 @@ protected: spv::ImageFormat TranslateImageFormat(const glslang::TType& type); spv::SelectionControlMask TranslateSelectionControl(const glslang::TIntermSelection&) const; spv::SelectionControlMask TranslateSwitchControl(const glslang::TIntermSwitch&) const; - spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, unsigned int& dependencyLength) const; + spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, std::vector& operands) const; spv::StorageClass TranslateStorageClass(const glslang::TType&); void addIndirectionIndexCapabilities(const glslang::TType& baseType, const glslang::TType& indexType); spv::Id createSpvVariable(const glslang::TIntermSymbol*); @@ -1055,7 +1055,7 @@ spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSwitchControl(const g // return a non-0 dependency if the dependency argument must be set spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode, - unsigned int& dependencyLength) const + std::vector& operands) const { spv::LoopControlMask control = spv::LoopControlMaskNone; @@ -1067,7 +1067,29 @@ spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang: control = control | spv::LoopControlDependencyInfiniteMask; else if (loopNode.getLoopDependency() > 0) { control = control | spv::LoopControlDependencyLengthMask; - dependencyLength = loopNode.getLoopDependency(); + operands.push_back((unsigned int)loopNode.getLoopDependency()); + } + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { + if (loopNode.getMinIterations() > 0) { + control = control | spv::LoopControlMinIterationsMask; + operands.push_back(loopNode.getMinIterations()); + } + if (loopNode.getMaxIterations() < glslang::TIntermLoop::iterationsInfinite) { + control = control | spv::LoopControlMaxIterationsMask; + operands.push_back(loopNode.getMaxIterations()); + } + if (loopNode.getIterationMultiple() > 1) { + control = control | spv::LoopControlIterationMultipleMask; + operands.push_back(loopNode.getIterationMultiple()); + } + if (loopNode.getPeelCount() > 0) { + control = control | spv::LoopControlPeelCountMask; + operands.push_back(loopNode.getPeelCount()); + } + if (loopNode.getPartialCount() > 0) { + control = control | spv::LoopControlPartialCountMask; + operands.push_back(loopNode.getPartialCount()); + } } return control; @@ -2841,8 +2863,8 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn builder.createBranch(&blocks.head); // Loop control: - unsigned int dependencyLength = glslang::TIntermLoop::dependencyInfinite; - const spv::LoopControlMask control = TranslateLoopControl(*node, dependencyLength); + std::vector operands; + const spv::LoopControlMask control = TranslateLoopControl(*node, operands); // 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 @@ -2852,7 +2874,7 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn // including merges of its own. builder.setLine(node->getLoc().line, node->getLoc().getFilename()); builder.setBuildPoint(&blocks.head); - builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control, dependencyLength); + builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control, operands); if (node->testFirst() && node->getTest()) { spv::Block& test = builder.makeNewBlock(); builder.createBranch(&test); diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp index 138c41c5f..6eb18d04b 100644 --- a/SPIRV/SpvBuilder.cpp +++ b/SPIRV/SpvBuilder.cpp @@ -2956,14 +2956,14 @@ void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control) } void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, - unsigned int dependencyLength) + const std::vector& operands) { Instruction* merge = new Instruction(OpLoopMerge); merge->addIdOperand(mergeBlock->getId()); merge->addIdOperand(continueBlock->getId()); merge->addImmediateOperand(control); - if ((control & LoopControlDependencyLengthMask) != 0) - merge->addImmediateOperand(dependencyLength); + for (int op = 0; op < (int)operands.size(); ++op) + merge->addImmediateOperand(operands[op]); buildPoint->addInstruction(std::unique_ptr(merge)); } diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h index 52f7fba76..72018a3ff 100644 --- a/SPIRV/SpvBuilder.h +++ b/SPIRV/SpvBuilder.h @@ -662,7 +662,7 @@ public: void createBranch(Block* block); void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock); - void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, unsigned int dependencyLength); + void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, const std::vector& operands); // Sets to generate opcode for specialization constants. void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; } diff --git a/SPIRV/doc.cpp b/SPIRV/doc.cpp index 76e1df8d2..4c2784356 100644 --- a/SPIRV/doc.cpp +++ b/SPIRV/doc.cpp @@ -674,15 +674,20 @@ const char* SelectControlString(int cont) } } -const int LoopControlCeiling = 4; +const int LoopControlCeiling = LoopControlPartialCountShift + 1; const char* LoopControlString(int cont) { switch (cont) { - case 0: return "Unroll"; - case 1: return "DontUnroll"; - case 2: return "DependencyInfinite"; - case 3: return "DependencyLength"; + case LoopControlUnrollShift: return "Unroll"; + case LoopControlDontUnrollShift: return "DontUnroll"; + case LoopControlDependencyInfiniteShift: return "DependencyInfinite"; + case LoopControlDependencyLengthShift: return "DependencyLength"; + case LoopControlMinIterationsShift: return "MinIterations"; + case LoopControlMaxIterationsShift: return "MaxIterations"; + case LoopControlIterationMultipleShift: return "IterationMultiple"; + case LoopControlPeelCountShift: return "PeelCount"; + case LoopControlPartialCountShift: return "PartialCount"; case LoopControlCeiling: default: return "Bad"; diff --git a/Test/baseResults/460.frag.out b/Test/baseResults/460.frag.out index 90c4837e2..8670e6e14 100644 --- a/Test/baseResults/460.frag.out +++ b/Test/baseResults/460.frag.out @@ -56,7 +56,7 @@ ERROR: node is still EOpNull! 0:28 Function Definition: attExt( ( global void) 0:28 Function Parameters: 0:30 Sequence -0:30 Loop with condition not tested first: Dependency -3 +0:30 Loop with condition not tested first 0:30 Loop Condition 0:30 Constant: 0:30 true (const bool) diff --git a/Test/baseResults/spv.1.4.LoopControl.frag.out b/Test/baseResults/spv.1.4.LoopControl.frag.out new file mode 100644 index 000000000..608b4bfc6 --- /dev/null +++ b/Test/baseResults/spv.1.4.LoopControl.frag.out @@ -0,0 +1,110 @@ +spv.1.4.LoopControl.frag +WARNING: 0:15: 'min_iterations' : expected a single integer argument +WARNING: 0:15: 'max_iterations' : expected a single integer argument + +Validation failed +// Module Version 10400 +// Generated by (magic number): 80007 +// Id's are bound by 54 + + Capability Shader + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 4 "main" 53 + ExecutionMode 4 OriginUpperLeft + Source GLSL 450 + SourceExtension "GL_EXT_control_flow_attributes" + Name 4 "main" + Name 8 "i" + Name 32 "i" + Name 42 "i" + Name 53 "cond" + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeInt 32 1 + 7: TypePointer Function 6(int) + 9: 6(int) Constant 0 + 16: 6(int) Constant 8 + 17: TypeBool + 20: 6(int) Constant 1 + 27: 17(bool) ConstantTrue + 52: TypePointer Private 17(bool) + 53(cond): 52(ptr) Variable Private + 4(main): 2 Function None 3 + 5: Label + 8(i): 7(ptr) Variable Function + 32(i): 7(ptr) Variable Function + 42(i): 7(ptr) Variable Function + Store 8(i) 9 + Branch 10 + 10: Label + LoopMerge 12 13 MinIterations MaxIterations 3 7 + Branch 14 + 14: Label + 15: 6(int) Load 8(i) + 18: 17(bool) SLessThan 15 16 + BranchConditional 18 11 12 + 11: Label + Branch 13 + 13: Label + 19: 6(int) Load 8(i) + 21: 6(int) IAdd 19 20 + Store 8(i) 21 + Branch 10 + 12: Label + Branch 22 + 22: Label + LoopMerge 24 25 IterationMultiple 2 + Branch 26 + 26: Label + BranchConditional 27 23 24 + 23: Label + Branch 25 + 25: Label + Branch 22 + 24: Label + Branch 28 + 28: Label + LoopMerge 30 31 PeelCount 5 + Branch 29 + 29: Label + Branch 31 + 31: Label + BranchConditional 27 28 30 + 30: Label + Store 32(i) 9 + Branch 33 + 33: Label + LoopMerge 35 36 PartialCount 4 + Branch 37 + 37: Label + 38: 6(int) Load 32(i) + 39: 17(bool) SLessThan 38 16 + BranchConditional 39 34 35 + 34: Label + Branch 36 + 36: Label + 40: 6(int) Load 32(i) + 41: 6(int) IAdd 40 20 + Store 32(i) 41 + Branch 33 + 35: Label + Store 42(i) 9 + Branch 43 + 43: Label + LoopMerge 45 46 None + Branch 47 + 47: Label + 48: 6(int) Load 42(i) + 49: 17(bool) SLessThan 48 16 + BranchConditional 49 44 45 + 44: Label + Branch 46 + 46: Label + 50: 6(int) Load 42(i) + 51: 6(int) IAdd 50 20 + Store 42(i) 51 + Branch 43 + 45: Label + Return + FunctionEnd diff --git a/Test/baseResults/spv.controlFlowAttributes.frag.out b/Test/baseResults/spv.controlFlowAttributes.frag.out index 489522be3..c7082326a 100644 --- a/Test/baseResults/spv.controlFlowAttributes.frag.out +++ b/Test/baseResults/spv.controlFlowAttributes.frag.out @@ -1,7 +1,7 @@ spv.controlFlowAttributes.frag -WARNING: 0:20: '' : attribute with arguments not recognized, skipping -WARNING: 0:21: '' : attribute with arguments not recognized, skipping -WARNING: 0:22: '' : attribute with arguments not recognized, skipping +WARNING: 0:20: 'unroll' : expected no arguments +WARNING: 0:21: 'dont_unroll' : expected no arguments +WARNING: 0:22: 'dependency_infinite' : expected no arguments WARNING: 0:23: 'dependency_length' : expected a single integer argument WARNING: 0:24: '' : attribute with arguments not recognized, skipping WARNING: 0:25: '' : attribute with arguments not recognized, skipping diff --git a/Test/spv.1.4.LoopControl.frag b/Test/spv.1.4.LoopControl.frag new file mode 100644 index 000000000..00392ae50 --- /dev/null +++ b/Test/spv.1.4.LoopControl.frag @@ -0,0 +1,19 @@ +#version 450 + +#extension GL_EXT_control_flow_attributes : enable + +bool cond; + +void main() +{ + [[min_iterations(3), max_iterations(7)]] for (int i = 0; i < 8; ++i) { } + [[iteration_multiple(2)]] while(true) { } + [[peel_count(5)]] do { } while(true); + [[partial_count(4)]] for (int i = 0; i < 8; ++i) { } + + // warnings on all these + [[min_iterations, max_iterations]] for (int i = 0; i < 8; ++i) { } + //[[iteration_multiple(0)]] while(true) { } + //[[peel_count]] do { } while(true); + //[[partial_count]] for (int i = 0; i < 8; ++i) { } +} diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h index a20490422..89d195495 100644 --- a/glslang/Include/intermediate.h +++ b/glslang/Include/intermediate.h @@ -1116,7 +1116,12 @@ public: first(testFirst), unroll(false), dontUnroll(false), - dependency(0) + dependency(0), + minIterations(0), + maxIterations(iterationsInfinite), + iterationMultiple(1), + peelCount(0), + partialCount(0) { } virtual TIntermLoop* getAsLoopNode() { return this; } @@ -1128,14 +1133,36 @@ public: bool testFirst() const { return first; } void setUnroll() { unroll = true; } - void setDontUnroll() { dontUnroll = true; } + void setDontUnroll() { + dontUnroll = true; + peelCount = 0; + partialCount = 0; + } bool getUnroll() const { return unroll; } bool getDontUnroll() const { return dontUnroll; } static const unsigned int dependencyInfinite = 0xFFFFFFFF; + static const unsigned int iterationsInfinite = 0xFFFFFFFF; void setLoopDependency(int d) { dependency = d; } int getLoopDependency() const { return dependency; } + void setMinIterations(unsigned int v) { minIterations = v; } + unsigned int getMinIterations() const { return minIterations; } + void setMaxIterations(unsigned int v) { maxIterations = v; } + unsigned int getMaxIterations() const { return maxIterations; } + void setIterationMultiple(unsigned int v) { iterationMultiple = v; } + unsigned int getIterationMultiple() const { return iterationMultiple; } + void setPeelCount(unsigned int v) { + peelCount = v; + dontUnroll = false; + } + unsigned int getPeelCount() const { return peelCount; } + void setPartialCount(unsigned int v) { + partialCount = v; + dontUnroll = false; + } + unsigned int getPartialCount() const { return partialCount; } + protected: TIntermNode* body; // code to loop over TIntermTyped* test; // exit condition associated with loop, could be 0 for 'for' loops @@ -1144,6 +1171,11 @@ protected: bool unroll; // true if unroll requested bool dontUnroll; // true if request to not unroll unsigned int dependency; // loop dependency hint; 0 means not set or unknown + unsigned int minIterations; // as per the SPIR-V specification + unsigned int maxIterations; // as per the SPIR-V specification + unsigned int iterationMultiple; // as per the SPIR-V specification + unsigned int peelCount; // as per the SPIR-V specification + unsigned int partialCount; // as per the SPIR-V specification }; // diff --git a/glslang/MachineIndependent/attribute.cpp b/glslang/MachineIndependent/attribute.cpp index bf960ffe6..d4a23f39d 100644 --- a/glslang/MachineIndependent/attribute.cpp +++ b/glslang/MachineIndependent/attribute.cpp @@ -52,6 +52,7 @@ bool TAttributeArgs::getInt(int& value, int argNum) const 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 @@ -110,6 +111,16 @@ TAttributeType TParseContext::attributeFromName(const TString& name) const return EatDependencyInfinite; else if (name == "dependency_length") return EatDependencyLength; + else if (name == "min_iterations") + return EatMinIterations; + else if (name == "max_iterations") + return EatMaxIterations; + else if (name == "iteration_multiple") + return EatIterationMultiple; + else if (name == "peel_count") + return EatPeelCount; + else if (name == "partial_count") + return EatPartialCount; else return EatNone; } @@ -225,29 +236,101 @@ void TParseContext::handleLoopAttributes(const TAttributes& attributes, TIntermN } for (auto it = attributes.begin(); it != attributes.end(); ++it) { - if (it->name != EatDependencyLength && it->size() > 0) { - warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", ""); - continue; - } - int value; + const auto noArgument = [&](const char* feature) { + if (it->size() > 0) { + warn(node->getLoc(), "expected no arguments", feature, ""); + return false; + } + return true; + }; + + const auto positiveSignedArgument = [&](const char* feature, int& value) { + if (it->size() == 1 && it->getInt(value)) { + if (value <= 0) { + error(node->getLoc(), "must be positive", feature, ""); + return false; + } + } else { + warn(node->getLoc(), "expected a single integer argument", feature, ""); + return false; + } + return true; + }; + + const auto unsignedArgument = [&](const char* feature, unsigned int& uiValue) { + int value; + if (!(it->size() == 1 && it->getInt(value))) { + warn(node->getLoc(), "expected a single integer argument", feature, ""); + return false; + } + uiValue = (unsigned int)value; + return true; + }; + + const auto positiveUnsignedArgument = [&](const char* feature, unsigned int& uiValue) { + int value; + if (it->size() == 1 && it->getInt(value)) { + if (value == 0) { + error(node->getLoc(), "must be greater than or equal to 1", feature, ""); + return false; + } + } else { + warn(node->getLoc(), "expected a single integer argument", feature, ""); + return false; + } + uiValue = (unsigned int)value; + return true; + }; + + const auto spirv14 = [&](const char* feature) { + if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4) + warn(node->getLoc(), "attribute requires a SPIR-V 1.4 target-env", feature, ""); + }; + + int value = 0; + unsigned uiValue = 0; switch (it->name) { case EatUnroll: - loop->setUnroll(); + if (noArgument("unroll")) + loop->setUnroll(); break; case EatLoop: - loop->setDontUnroll(); + if (noArgument("dont_unroll")) + loop->setDontUnroll(); break; case EatDependencyInfinite: - loop->setLoopDependency(TIntermLoop::dependencyInfinite); + if (noArgument("dependency_infinite")) + loop->setLoopDependency(TIntermLoop::dependencyInfinite); break; case EatDependencyLength: - if (it->size() == 1 && it->getInt(value)) { - if (value <= 0) - error(node->getLoc(), "must be positive", "dependency_length", ""); + if (positiveSignedArgument("dependency_length", value)) loop->setLoopDependency(value); - } else - warn(node->getLoc(), "expected a single integer argument", "dependency_length", ""); + break; + case EatMinIterations: + spirv14("min_iterations"); + if (unsignedArgument("min_iterations", uiValue)) + loop->setMinIterations(uiValue); + break; + case EatMaxIterations: + spirv14("max_iterations"); + if (unsignedArgument("max_iterations", uiValue)) + loop->setMaxIterations(uiValue); + break; + case EatIterationMultiple: + spirv14("iteration_multiple"); + if (positiveUnsignedArgument("iteration_multiple", uiValue)) + loop->setIterationMultiple(uiValue); + break; + case EatPeelCount: + spirv14("peel_count"); + if (unsignedArgument("peel_count", uiValue)) + loop->setPeelCount(uiValue); + break; + case EatPartialCount: + spirv14("partial_count"); + if (unsignedArgument("partial_count", uiValue)) + loop->setPartialCount(uiValue); break; default: warn(node->getLoc(), "attribute does not apply to a loop", "", ""); diff --git a/glslang/MachineIndependent/attribute.h b/glslang/MachineIndependent/attribute.h index 8d0c5bcaf..844ce4580 100644 --- a/glslang/MachineIndependent/attribute.h +++ b/glslang/MachineIndependent/attribute.h @@ -71,7 +71,12 @@ namespace glslang { EatPushConstant, EatConstantId, EatDependencyInfinite, - EatDependencyLength + EatDependencyLength, + EatMinIterations, + EatMaxIterations, + EatIterationMultiple, + EatPeelCount, + EatPartialCount }; class TIntermAggregate; diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp index b8427c82f..59789597d 100755 --- a/gtests/Spv.FromFile.cpp +++ b/gtests/Spv.FromFile.cpp @@ -467,6 +467,7 @@ INSTANTIATE_TEST_CASE_P( ::testing::ValuesIn(std::vector({ "spv.1.4.OpEntryPoint.frag", "spv.1.4.OpSelect.frag", + "spv.1.4.LoopControl.frag", })), FileNameAsCustomTestSuffix );