GLSL: Implement GL_EXT_control_flow_attributes.

See https://github.com/KhronosGroup/GLSL/pull/11.
This commit is contained in:
John Kessenich 2018-01-31 08:11:18 -07:00
parent e18fd20d5c
commit a2858d9bdd
21 changed files with 3120 additions and 2405 deletions

View File

@ -131,7 +131,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&) const;
spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, unsigned int& dependencyLength) const;
spv::StorageClass TranslateStorageClass(const glslang::TType&);
spv::Id createSpvVariable(const glslang::TIntermSymbol*);
spv::Id getSampledType(const glslang::TSampler&);
@ -767,7 +767,9 @@ spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSwitchControl(const g
return spv::SelectionControlMaskNone;
}
spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode) const
// return a non-0 dependency if the dependency argument must be set
spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode,
unsigned int& dependencyLength) const
{
spv::LoopControlMask control = spv::LoopControlMaskNone;
@ -775,6 +777,12 @@ spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang:
control = control | spv::LoopControlDontUnrollMask;
if (loopNode.getUnroll())
control = control | spv::LoopControlUnrollMask;
if (loopNode.getLoopDependency() == glslang::TIntermLoop::dependencyInfinite)
control = control | spv::LoopControlDependencyInfiniteMask;
else if (loopNode.getLoopDependency() > 0) {
control = control | spv::LoopControlDependencyLengthMask;
dependencyLength = loopNode.getLoopDependency();
}
return control;
}
@ -2137,9 +2145,8 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn
builder.createBranch(&blocks.head);
// Loop control:
const spv::LoopControlMask control = TranslateLoopControl(*node);
// TODO: dependency length
unsigned int dependencyLength = glslang::TIntermLoop::dependencyInfinite;
const spv::LoopControlMask control = TranslateLoopControl(*node, dependencyLength);
// 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
@ -2149,7 +2156,7 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn
// including merges of its own.
builder.setLine(node->getLoc().line);
builder.setBuildPoint(&blocks.head);
builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control);
builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control, dependencyLength);
if (node->testFirst() && node->getTest()) {
spv::Block& test = builder.makeNewBlock();
builder.createBranch(&test);

View File

@ -2573,12 +2573,15 @@ void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control)
buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
}
void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control)
void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
unsigned int dependencyLength)
{
Instruction* merge = new Instruction(OpLoopMerge);
merge->addIdOperand(mergeBlock->getId());
merge->addIdOperand(continueBlock->getId());
merge->addImmediateOperand(control);
if ((control & LoopControlDependencyLengthMask) != 0)
merge->addImmediateOperand(dependencyLength);
buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
}

View File

@ -561,7 +561,7 @@ public:
void createBranch(Block* block);
void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control);
void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, unsigned int dependencyLength);
// Sets to generate opcode for specialization constants.
void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }

View File

@ -2558,6 +2558,7 @@ void Parameterize()
InstructionDesc[OpLoopMerge].operands.push(OperandId, "'Merge Block'");
InstructionDesc[OpLoopMerge].operands.push(OperandId, "'Continue Target'");
InstructionDesc[OpLoopMerge].operands.push(OperandLoop, "");
InstructionDesc[OpLoopMerge].operands.push(OperandOptionalLiteral, "");
InstructionDesc[OpSelectionMerge].operands.push(OperandId, "'Merge Block'");
InstructionDesc[OpSelectionMerge].operands.push(OperandSelect, "");

View File

@ -15,3 +15,18 @@ void main()
b1 = allInvocations(b1);
b1 = allInvocationsEqual(b1);
}
void attExtBad()
{
// ERRORs, not enabled
[[dependency_length(1+3)]] for (int i = 0; i < 8; ++i) { }
[[flatten]] if (true) { } else { }
}
#extension GL_EXT_control_flow_attributes : enable
void attExt()
{
[[dependency_length(-3)]] do { } while(true); // ERROR, not positive
[[dependency_length(0)]] do { } while(true); // ERROR, not positive
}

View File

@ -1,6 +1,14 @@
460.frag
ERROR: 0:22: 'attribute' : required extension not requested: GL_EXT_control_flow_attributes
ERROR: 0:23: 'attribute' : required extension not requested: GL_EXT_control_flow_attributes
ERROR: 0:30: 'dependency_length' : must be positive
ERROR: 0:31: 'dependency_length' : must be positive
ERROR: 4 compilation errors. No code generated.
Shader version: 460
0:? Sequence
Requested GL_EXT_control_flow_attributes
ERROR: node is still EOpNull!
0:10 Function Definition: main( ( global void)
0:10 Function Parameters:
0:12 Sequence
@ -21,6 +29,43 @@ Shader version: 460
0:16 'b1' ( temp bool)
0:16 allInvocationsEqual ( global bool)
0:16 'b1' ( temp bool)
0:19 Function Definition: attExtBad( ( global void)
0:19 Function Parameters:
0:22 Sequence
0:22 Sequence
0:22 Sequence
0:22 move second child to first child ( temp int)
0:22 'i' ( temp int)
0:22 Constant:
0:22 0 (const int)
0:22 Loop with condition tested first
0:22 Loop Condition
0:22 Compare Less Than ( temp bool)
0:22 'i' ( temp int)
0:22 Constant:
0:22 8 (const int)
0:22 No loop body
0:22 Loop Terminal Expression
0:22 Pre-Increment ( temp int)
0:22 'i' ( temp int)
0:23 Test condition and select ( temp void)
0:23 Condition
0:23 Constant:
0:23 true (const bool)
0:23 true case is null
0:28 Function Definition: attExt( ( global void)
0:28 Function Parameters:
0:30 Sequence
0:30 Loop with condition not tested first
0:30 Loop Condition
0:30 Constant:
0:30 true (const bool)
0:30 No loop body
0:31 Loop with condition not tested first
0:31 Loop Condition
0:31 Constant:
0:31 true (const bool)
0:31 No loop body
0:? Linker Objects
0:? 's' ( smooth in structure{ global float f, global 4-component vector of float v})
@ -29,7 +74,8 @@ Linked fragment stage:
Shader version: 460
0:? Sequence
Requested GL_EXT_control_flow_attributes
ERROR: node is still EOpNull!
0:10 Function Definition: main( ( global void)
0:10 Function Parameters:
0:12 Sequence

View File

@ -0,0 +1,238 @@
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: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
WARNING: 0:26: '' : attribute with arguments not recognized, skipping
// Module Version 10000
// Generated by (magic number): 80003
// Id's are bound by 118
Capability Shader
1: ExtInstImport "GLSL.std.450"
MemoryModel Logical GLSL450
EntryPoint Fragment 4 "main"
ExecutionMode 4 OriginUpperLeft
Source GLSL 450
SourceExtension "GL_EXT_control_flow_attributes"
Name 4 "main"
Name 8 "i"
Name 36 "i"
Name 47 "cond"
Name 60 "i"
Name 79 "i"
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
31: 17(bool) ConstantTrue
46: TypePointer Private 17(bool)
47(cond): 46(ptr) Variable Private
54: 17(bool) ConstantFalse
55: 6(int) Constant 3
4(main): 2 Function None 3
5: Label
8(i): 7(ptr) Variable Function
36(i): 7(ptr) Variable Function
60(i): 7(ptr) Variable Function
79(i): 7(ptr) Variable Function
Store 8(i) 9
Branch 10
10: Label
LoopMerge 12 13 Unroll
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 DontUnroll
Branch 23
23: Label
Branch 25
25: Label
Branch 22
24: Label
Branch 26
26: Label
LoopMerge 28 29 DontUnroll
Branch 30
30: Label
BranchConditional 31 27 28
27: Label
Branch 29
29: Label
Branch 26
28: Label
Branch 32
32: Label
LoopMerge 34 35 DependencyInfinite
Branch 33
33: Label
Branch 35
35: Label
BranchConditional 31 32 34
34: Label
Store 36(i) 9
Branch 37
37: Label
LoopMerge 39 40 DependencyLength 4
Branch 41
41: Label
42: 6(int) Load 36(i)
43: 17(bool) SLessThan 42 16
BranchConditional 43 38 39
38: Label
Branch 40
40: Label
44: 6(int) Load 36(i)
45: 6(int) IAdd 44 20
Store 36(i) 45
Branch 37
39: Label
48: 17(bool) Load 47(cond)
SelectionMerge 50 Flatten
BranchConditional 48 49 50
49: Label
Branch 50
50: Label
51: 17(bool) Load 47(cond)
SelectionMerge 53 DontFlatten
BranchConditional 51 52 53
52: Label
Store 47(cond) 54
Branch 53
53: Label
SelectionMerge 57 DontFlatten
Switch 55 57
case 3: 56
56: Label
Branch 57
57: Label
Store 60(i) 9
Branch 61
61: Label
LoopMerge 63 64 None
Branch 65
65: Label
66: 6(int) Load 60(i)
67: 17(bool) SLessThan 66 16
BranchConditional 67 62 63
62: Label
Branch 64
64: Label
68: 6(int) Load 60(i)
69: 6(int) IAdd 68 20
Store 60(i) 69
Branch 61
63: Label
Branch 70
70: Label
LoopMerge 72 73 None
Branch 74
74: Label
BranchConditional 31 71 72
71: Label
Branch 73
73: Label
Branch 70
72: Label
Branch 75
75: Label
LoopMerge 77 78 None
Branch 76
76: Label
Branch 78
78: Label
BranchConditional 31 75 77
77: Label
Store 79(i) 9
Branch 80
80: Label
LoopMerge 82 83 None
Branch 84
84: Label
85: 6(int) Load 79(i)
86: 17(bool) SLessThan 85 16
BranchConditional 86 81 82
81: Label
Branch 83
83: Label
87: 6(int) Load 79(i)
88: 6(int) IAdd 87 20
Store 79(i) 88
Branch 80
82: Label
89: 17(bool) Load 47(cond)
SelectionMerge 91 None
BranchConditional 89 90 91
90: Label
Branch 91
91: Label
92: 17(bool) Load 47(cond)
SelectionMerge 94 None
BranchConditional 92 93 94
93: Label
Store 47(cond) 54
Branch 94
94: Label
SelectionMerge 96 None
Switch 55 96
case 3: 95
95: Label
Branch 96
96: Label
Branch 99
99: Label
LoopMerge 101 102 Unroll DontUnroll DependencyLength 2
Branch 103
103: Label
104: 17(bool) Load 47(cond)
BranchConditional 104 100 101
100: Label
Branch 102
102: Label
Branch 99
101: Label
SelectionMerge 106 DontFlatten
Switch 55 106
case 3: 105
105: Label
Branch 106
106: Label
109: 17(bool) Load 47(cond)
SelectionMerge 111 Flatten
BranchConditional 109 110 111
110: Label
Branch 111
111: Label
Branch 112
112: Label
LoopMerge 114 115 DependencyInfinite
Branch 116
116: Label
117: 17(bool) Load 47(cond)
BranchConditional 117 113 114
113: Label
Branch 115
115: Label
Branch 112
114: Label
Return
FunctionEnd

View File

@ -0,0 +1,39 @@
#version 450
#extension GL_EXT_control_flow_attributes : enable
bool cond;
void main()
{
[[unroll]] for (int i = 0; i < 8; ++i) { }
[[loop]] for (;;) { }
[[dont_unroll]] while(true) { }
[[dependency_infinite]] do { } while(true);
[[dependency_length(1+3)]] for (int i = 0; i < 8; ++i) { }
[[flatten]] if (cond) { } else { }
[[branch]] if (cond) cond = false;
[[dont_flatten]] switch(3) { } // dropped
[[dont_flatten]] switch(3) { case 3: break; }
// warnings on all these
[[unroll(2)]] for (int i = 0; i < 8; ++i) { }
[[dont_unroll(-2)]] while(true) { }
[[dependency_infinite(3)]] do { } while(true);
[[dependency_length]] for (int i = 0; i < 8; ++i) { }
[[flatten(3)]] if (cond) { } else { }
[[branch(5.2)]] if (cond) cond = false;
[[dont_flatten(3 + 7)]] switch(3) { case 3: break; }
// other valid uses
[[ unroll, dont_unroll, dependency_length(2) ]] while(cond) { }
[ [ dont_flatten , branch ] ] switch(3) { case 3: break; }
[
// attribute
[
// here
flatten
]
] if (cond) { } else { }
[[ dependency_length(2), dependency_infinite ]] while(cond) { }
}

View File

@ -155,7 +155,7 @@ inline TString* NewPoolTString(const char* s)
return new(memory) TString(s);
}
template<class T> inline T* NewPoolObject(T)
template<class T> inline T* NewPoolObject(T*)
{
return new(GetThreadPoolAllocator().allocate(sizeof(T))) T;
}

View File

@ -896,7 +896,8 @@ public:
terminal(aTerminal),
first(testFirst),
unroll(false),
dontUnroll(false)
dontUnroll(false),
dependency(0)
{ }
virtual TIntermLoop* getAsLoopNode() { return this; }
@ -912,6 +913,10 @@ public:
bool getUnroll() const { return unroll; }
bool getDontUnroll() const { return dontUnroll; }
static const unsigned int dependencyInfinite = 0xFFFFFFFF;
void setLoopDependency(int d) { dependency = d; }
int getLoopDependency() const { return dependency; }
protected:
TIntermNode* body; // code to loop over
TIntermTyped* test; // exit condition associated with loop, could be 0 for 'for' loops
@ -919,6 +924,7 @@ protected:
bool first; // true for while and for, not for do-while
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
};
//

View File

@ -44,13 +44,15 @@
#ifndef _PARSER_HELPER_INCLUDED_
#define _PARSER_HELPER_INCLUDED_
#include <cstdarg>
#include <functional>
#include "parseVersions.h"
#include "../Include/ShHandle.h"
#include "SymbolTable.h"
#include "localintermediate.h"
#include "Scan.h"
#include <cstdarg>
#include <functional>
#include "attribute.h"
namespace glslang {
@ -409,6 +411,17 @@ public:
TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body);
void updateImplicitArraySize(const TSourceLoc&, TIntermNode*, int index);
TAttributeType attributeFromName(const TString& name) const;
TAttributes* makeAttributes(const TString& identifier) const;
TAttributes* makeAttributes(const TString& identifier, TIntermNode* node) const;
TAttributes* mergeAttributes(TAttributes*, TAttributes*) const;
// Determine selection control from attributes
void handleSelectionAttributes(const TAttributes& attributes, TIntermNode*);
void handleSwitchAttributes(const TAttributes& attributes, TIntermNode*);
// Determine loop control from attributes
void handleLoopAttributes(const TAttributes& attributes, TIntermNode*);
protected:
void nonInitConstCheck(const TSourceLoc&, TString& identifier, TType& type);

View File

@ -45,6 +45,7 @@
#include "../Include/Types.h"
#include "SymbolTable.h"
#include "ParseHelper.h"
#include "attribute.h"
#include "glslang_tab.cpp.h"
#include "ScanContext.h"
#include "Scan.h"

View File

@ -188,6 +188,7 @@ void TParseVersions::initializeExtensionBehavior()
extensionBehavior[E_GL_EXT_shader_non_constant_global_initializers] = EBhDisable;
extensionBehavior[E_GL_EXT_shader_image_load_formatted] = EBhDisable;
extensionBehavior[E_GL_EXT_post_depth_coverage] = EBhDisable;
extensionBehavior[E_GL_EXT_control_flow_attributes] = EBhDisable;
// #line and #include
extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhDisable;
@ -328,6 +329,7 @@ void TParseVersions::getPreamble(std::string& preamble)
"#define GL_EXT_shader_non_constant_global_initializers 1\n"
"#define GL_EXT_shader_image_load_formatted 1\n"
"#define GL_EXT_post_depth_coverage 1\n"
"#define GL_EXT_control_flow_attributes 1\n"
#ifdef AMD_EXTENSIONS
"#define GL_AMD_shader_ballot 1\n"

View File

@ -146,6 +146,7 @@ const char* const E_GL_EXT_shader_image_load_formatted = "GL_EXT_shader_image_lo
const char* const E_GL_EXT_device_group = "GL_EXT_device_group";
const char* const E_GL_EXT_multiview = "GL_EXT_multiview";
const char* const E_GL_EXT_post_depth_coverage = "GL_EXT_post_depth_coverage";
const char* const E_GL_EXT_control_flow_attributes = "GL_EXT_control_flow_attributes";
// Arrays of extensions for the above viewportEXTs duplications

View File

@ -40,47 +40,218 @@
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);
// 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;
if (intConst == nullptr)
return false;
value = intConst->getIConst();
return true;
};
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);
// 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;
if (stringConst == nullptr)
return false;
value = *stringConst->getSConst();
value = *stringConst->getSConst();
// Convenience.
if (convertToLower)
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
// Convenience.
if (convertToLower)
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
return true;
};
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;
// How many arguments were supplied?
int TAttributeArgs::size() const
{
return args == nullptr ? 0 : (int)args->getSequence().size();
}
const TConstUnion* constVal = &args->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0];
if (constVal == nullptr || constVal->getType() != basicType)
return nullptr;
// Helper to get attribute const union. Returns nullptr on failure.
const TConstUnion* TAttributeArgs::getConstUnion(TBasicType basicType, int argNum) const
{
if (args == nullptr)
return nullptr;
return constVal;
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;
}
// Implementation of TParseContext parts of attributes
TAttributeType TParseContext::attributeFromName(const TString& name) const
{
if (name == "branch" || name == "dont_flatten")
return EatBranch;
else if (name == "flatten")
return EatFlatten;
else if (name == "unroll")
return EatUnroll;
else if (name == "loop" || name == "dont_unroll")
return EatLoop;
else if (name == "dependency_infinite")
return EatDependencyInfinite;
else if (name == "dependency_length")
return EatDependencyLength;
else
return EatNone;
}
// Make an initial leaf for the grammar from a no-argument attribute
TAttributes* TParseContext::makeAttributes(const TString& identifier) const
{
TAttributes *attributes = nullptr;
attributes = NewPoolObject(attributes);
TAttributeArgs args = { attributeFromName(identifier), nullptr };
attributes->push_back(args);
return attributes;
}
// Make an initial leaf for the grammar from a one-argument attribute
TAttributes* TParseContext::makeAttributes(const TString& identifier, TIntermNode* node) const
{
TAttributes *attributes = nullptr;
attributes = NewPoolObject(attributes);
// for now, node is always a simple single expression, but other code expects
// a list, so make it so
TIntermAggregate* agg = intermediate.makeAggregate(node);
TAttributeArgs args = { attributeFromName(identifier), agg };
attributes->push_back(args);
return attributes;
}
// Merge two sets of attributes into a single set.
// The second argument is destructively consumed.
TAttributes* TParseContext::mergeAttributes(TAttributes* attr1, TAttributes* attr2) const
{
attr1->splice(attr1->end(), *attr2);
return attr1;
}
//
// Selection attributes
//
void TParseContext::handleSelectionAttributes(const TAttributes& attributes, TIntermNode* node)
{
TIntermSelection* selection = node->getAsSelectionNode();
if (selection == nullptr)
return;
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
if (it->size() > 0) {
warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
continue;
}
switch (it->name) {
case EatFlatten:
selection->setFlatten();
break;
case EatBranch:
selection->setDontFlatten();
break;
default:
warn(node->getLoc(), "attribute does not apply to a selection", "", "");
break;
}
}
}
//
// Switch attributes
//
void TParseContext::handleSwitchAttributes(const TAttributes& attributes, TIntermNode* node)
{
TIntermSwitch* selection = node->getAsSwitchNode();
if (selection == nullptr)
return;
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
if (it->size() > 0) {
warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
continue;
}
switch (it->name) {
case EatFlatten:
selection->setFlatten();
break;
case EatBranch:
selection->setDontFlatten();
break;
default:
warn(node->getLoc(), "attribute does not apply to a switch", "", "");
break;
}
}
}
//
// Loop attributes
//
void TParseContext::handleLoopAttributes(const TAttributes& attributes, TIntermNode* node)
{
TIntermLoop* loop = node->getAsLoopNode();
if (loop == nullptr) {
// the actual loop might be part of a sequence
TIntermAggregate* agg = node->getAsAggregate();
if (agg == nullptr)
return;
for (auto it = agg->getSequence().begin(); it != agg->getSequence().end(); ++it) {
loop = (*it)->getAsLoopNode();
if (loop != nullptr)
break;
}
if (loop == nullptr)
return;
}
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;
switch (it->name) {
case EatUnroll:
loop->setUnroll();
break;
case EatLoop:
loop->setDontUnroll();
break;
case EatDependencyInfinite:
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", "");
loop->setLoopDependency(value);
} else
warn(node->getLoc(), "expected a single integer argument", "dependency_length", "");
break;
default:
warn(node->getLoc(), "attribute does not apply to a loop", "", "");
break;
}
}
}
} // end namespace glslang

View File

@ -69,14 +69,16 @@ namespace glslang {
EatInputAttachment,
EatBuiltIn,
EatPushConstant,
EatConstantId
EatConstantId,
EatDependencyInfinite,
EatDependencyLength
};
class TIntermAggregate;
struct TAttributeArgs {
TAttributeType name;
TIntermAggregate* args;
const TIntermAggregate* args;
// Obtain attribute as integer
// Return false if it cannot be obtained
@ -86,6 +88,9 @@ namespace glslang {
// Return false if it cannot be obtained
bool getString(TString& value, int argNum = 0, bool convertToLower = true) const;
// How many arguments were provided to the attribute?
int size() const;
protected:
const TConstUnion* getConstUnion(TBasicType basicType, int argNum) const;
};

View File

@ -58,6 +58,7 @@ Jutta Degener, 1995
#include "SymbolTable.h"
#include "ParseHelper.h"
#include "../Public/ShaderLang.h"
#include "attribute.h"
using namespace glslang;
@ -86,6 +87,7 @@ using namespace glslang;
TIntermNode* intermNode;
glslang::TIntermNodePair nodePair;
glslang::TIntermTyped* intermTypedNode;
glslang::TAttributes* attributes;
};
union {
glslang::TPublicType type;
@ -218,12 +220,12 @@ extern int yylex(YYSTYPE*, TParseContext&);
%type <interm.intermNode> translation_unit function_definition
%type <interm.intermNode> statement simple_statement
%type <interm.intermNode> statement_list switch_statement_list compound_statement
%type <interm.intermNode> declaration_statement selection_statement expression_statement
%type <interm.intermNode> switch_statement case_label
%type <interm.intermNode> declaration_statement selection_statement selection_statement_nonattributed expression_statement
%type <interm.intermNode> switch_statement switch_statement_nonattributed case_label
%type <interm.intermNode> declaration external_declaration
%type <interm.intermNode> for_init_statement compound_statement_no_new_scope
%type <interm.nodePair> selection_rest_statement for_rest_statement
%type <interm.intermNode> iteration_statement jump_statement statement_no_new_scope statement_scoped
%type <interm.intermNode> iteration_statement iteration_statement_nonattributed jump_statement statement_no_new_scope statement_scoped
%type <interm> single_declaration init_declarator_list
%type <interm> parameter_declaration parameter_declarator parameter_type_specifier
@ -246,6 +248,8 @@ extern int yylex(YYSTYPE*, TParseContext&);
%type <interm.identifierList> identifier_list
%type <interm.attributes> attribute attribute_list single_attribute
%start translation_unit
%%
@ -2673,6 +2677,15 @@ expression_statement
;
selection_statement
: selection_statement_nonattributed {
$$ = $1;
}
| attribute selection_statement_nonattributed {
parseContext.handleSelectionAttributes(*$1, $2);
$$ = $2;
}
selection_statement_nonattributed
: IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement {
parseContext.boolCheck($1.loc, $3);
$$ = parseContext.intermediate.addSelection($3, $5, $1.loc);
@ -2709,6 +2722,15 @@ condition
;
switch_statement
: switch_statement_nonattributed {
$$ = $1;
}
| attribute switch_statement_nonattributed {
parseContext.handleSwitchAttributes(*$1, $2);
$$ = $2;
}
switch_statement_nonattributed
: SWITCH LEFT_PAREN expression RIGHT_PAREN {
// start new switch sequence on the switch stack
++parseContext.controlFlowNestingLevel;
@ -2762,6 +2784,15 @@ case_label
;
iteration_statement
: iteration_statement_nonattributed {
$$ = $1;
}
| attribute iteration_statement_nonattributed {
parseContext.handleLoopAttributes(*$1, $2);
$$ = $2;
}
iteration_statement_nonattributed
: WHILE LEFT_PAREN {
if (! parseContext.limits.whileLoops)
parseContext.error($1.loc, "while loops not available", "limitation", "");
@ -2920,4 +2951,26 @@ function_definition
}
;
attribute
: LEFT_BRACKET LEFT_BRACKET attribute_list RIGHT_BRACKET RIGHT_BRACKET {
$$ = $3;
parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_control_flow_attributes, "attribute");
}
attribute_list
: single_attribute {
$$ = $1;
}
| attribute_list COMMA single_attribute {
$$ = parseContext.mergeAttributes($1, $3);
}
single_attribute
: IDENTIFIER {
$$ = parseContext.makeAttributes(*$1.string);
}
| IDENTIFIER LEFT_PAREN constant_expression RIGHT_PAREN {
$$ = parseContext.makeAttributes(*$1.string, $3);
}
%%

File diff suppressed because it is too large Load Diff

View File

@ -348,7 +348,7 @@ extern int yydebug;
typedef union YYSTYPE YYSTYPE;
union YYSTYPE
{
#line 68 "MachineIndependent/glslang.y" /* yacc.c:1909 */
#line 69 "MachineIndependent/glslang.y" /* yacc.c:1909 */
struct {
glslang::TSourceLoc loc;
@ -370,6 +370,7 @@ union YYSTYPE
TIntermNode* intermNode;
glslang::TIntermNodePair nodePair;
glslang::TIntermTyped* intermTypedNode;
glslang::TAttributes* attributes;
};
union {
glslang::TPublicType type;
@ -382,7 +383,7 @@ union YYSTYPE
};
} interm;
#line 386 "MachineIndependent/glslang_tab.cpp.h" /* yacc.c:1909 */
#line 387 "MachineIndependent/glslang_tab.cpp.h" /* yacc.c:1909 */
};
# define YYSTYPE_IS_TRIVIAL 1
# define YYSTYPE_IS_DECLARED 1

View File

@ -235,6 +235,7 @@ INSTANTIATE_TEST_CASE_P(
"spv.branch-return.vert",
"spv.builtInXFB.vert",
"spv.conditionalDiscard.frag",
"spv.controlFlowAttributes.frag",
"spv.conversion.frag",
"spv.dataOut.frag",
"spv.dataOutIndirect.frag",

View File

@ -8868,7 +8868,7 @@ void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwit
}
//
// Loop hints
// Loop attributes
//
void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop,
const TAttributes& attributes)