SPV 1.4: Implement the 5 new loop controls.
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index d25ccd3..3d0f0c7 100644
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -135,7 +135,7 @@
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<unsigned int>& 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 @@
// 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<unsigned int>& operands) const
{
spv::LoopControlMask control = spv::LoopControlMaskNone;
@@ -1067,7 +1067,29 @@
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 @@
builder.createBranch(&blocks.head);
// Loop control:
- unsigned int dependencyLength = glslang::TIntermLoop::dependencyInfinite;
- const spv::LoopControlMask control = TranslateLoopControl(*node, dependencyLength);
+ std::vector<unsigned int> 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 @@
// 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 138c41c..6eb18d0 100644
--- a/SPIRV/SpvBuilder.cpp
+++ b/SPIRV/SpvBuilder.cpp
@@ -2956,14 +2956,14 @@
}
void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
- unsigned int dependencyLength)
+ const std::vector<unsigned int>& 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<Instruction>(merge));
}
diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h
index 52f7fba..72018a3 100644
--- a/SPIRV/SpvBuilder.h
+++ b/SPIRV/SpvBuilder.h
@@ -662,7 +662,7 @@
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<unsigned int>& operands);
// Sets to generate opcode for specialization constants.
void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }
diff --git a/SPIRV/doc.cpp b/SPIRV/doc.cpp
index 76e1df8..4c27843 100644
--- a/SPIRV/doc.cpp
+++ b/SPIRV/doc.cpp
@@ -674,15 +674,20 @@
}
}
-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 90c4837..8670e6e 100644
--- a/Test/baseResults/460.frag.out
+++ b/Test/baseResults/460.frag.out
@@ -56,7 +56,7 @@
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 0000000..608b4bf
--- /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 489522b..c708232 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 0000000..00392ae
--- /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 a204904..89d1954 100644
--- a/glslang/Include/intermediate.h
+++ b/glslang/Include/intermediate.h
@@ -1116,7 +1116,12 @@
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 @@
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 @@
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 bf960ff..d4a23f3 100644
--- a/glslang/MachineIndependent/attribute.cpp
+++ b/glslang/MachineIndependent/attribute.cpp
@@ -52,6 +52,7 @@
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 @@
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 @@
}
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 8d0c5bc..844ce45 100644
--- a/glslang/MachineIndependent/attribute.h
+++ b/glslang/MachineIndependent/attribute.h
@@ -71,7 +71,12 @@
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 b8427c8..5978959 100755
--- a/gtests/Spv.FromFile.cpp
+++ b/gtests/Spv.FromFile.cpp
@@ -467,6 +467,7 @@
::testing::ValuesIn(std::vector<std::string>({
"spv.1.4.OpEntryPoint.frag",
"spv.1.4.OpSelect.frag",
+ "spv.1.4.LoopControl.frag",
})),
FileNameAsCustomTestSuffix
);