mirror of
https://github.com/KhronosGroup/glslang
synced 2024-11-09 12:00:05 +00:00
SPV 1.4: Implement the 5 new loop controls.
This commit is contained in:
parent
0c1e71a123
commit
1f4d04687b
@ -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<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 @@ 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<unsigned int>& 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<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 @@ 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);
|
||||
|
@ -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<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));
|
||||
}
|
||||
|
||||
|
@ -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<unsigned int>& operands);
|
||||
|
||||
// Sets to generate opcode for specialization constants.
|
||||
void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }
|
||||
|
@ -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";
|
||||
|
@ -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)
|
||||
|
110
Test/baseResults/spv.1.4.LoopControl.frag.out
Normal file
110
Test/baseResults/spv.1.4.LoopControl.frag.out
Normal file
@ -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
|
@ -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
|
||||
|
19
Test/spv.1.4.LoopControl.frag
Normal file
19
Test/spv.1.4.LoopControl.frag
Normal file
@ -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) { }
|
||||
}
|
@ -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
|
||||
};
|
||||
|
||||
//
|
||||
|
@ -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", "", "");
|
||||
|
@ -71,7 +71,12 @@ namespace glslang {
|
||||
EatPushConstant,
|
||||
EatConstantId,
|
||||
EatDependencyInfinite,
|
||||
EatDependencyLength
|
||||
EatDependencyLength,
|
||||
EatMinIterations,
|
||||
EatMaxIterations,
|
||||
EatIterationMultiple,
|
||||
EatPeelCount,
|
||||
EatPartialCount
|
||||
};
|
||||
|
||||
class TIntermAggregate;
|
||||
|
@ -467,6 +467,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
::testing::ValuesIn(std::vector<std::string>({
|
||||
"spv.1.4.OpEntryPoint.frag",
|
||||
"spv.1.4.OpSelect.frag",
|
||||
"spv.1.4.LoopControl.frag",
|
||||
})),
|
||||
FileNameAsCustomTestSuffix
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user