HLSL: Refactor attribute implementation.

- make it sharable with GLSL
- correct the case insensitivity
- remove the map; queries are not needed, all entries need processing
- make it easier to build bottom up (will help GLSL parsing)
- support semantic checking and reporting
- allow front-end dependent semantics and attribute name mapping
This commit is contained in:
John Kessenich 2018-01-30 11:01:39 -07:00
parent e349af7e20
commit e18fd20d5c
17 changed files with 638 additions and 500 deletions

View File

@ -129,8 +129,9 @@ protected:
spv::Decoration TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier);
spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable, bool memberDeclaration);
spv::ImageFormat TranslateImageFormat(const glslang::TType& type);
spv::SelectionControlMask TranslateSelectionControl(glslang::TSelectionControl) const;
spv::LoopControlMask TranslateLoopControl(glslang::TLoopControl) const;
spv::SelectionControlMask TranslateSelectionControl(const glslang::TIntermSelection&) const;
spv::SelectionControlMask TranslateSwitchControl(const glslang::TIntermSwitch&) const;
spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&) const;
spv::StorageClass TranslateStorageClass(const glslang::TType&);
spv::Id createSpvVariable(const glslang::TIntermSymbol*);
spv::Id getSampledType(const glslang::TSampler&);
@ -748,26 +749,34 @@ spv::ImageFormat TGlslangToSpvTraverser::TranslateImageFormat(const glslang::TTy
}
}
spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSelectionControl(glslang::TSelectionControl selectionControl) const
spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSelectionControl(const glslang::TIntermSelection& selectionNode) const
{
switch (selectionControl) {
case glslang::ESelectionControlNone: return spv::SelectionControlMaskNone;
case glslang::ESelectionControlFlatten: return spv::SelectionControlFlattenMask;
case glslang::ESelectionControlDontFlatten: return spv::SelectionControlDontFlattenMask;
default: return spv::SelectionControlMaskNone;
}
if (selectionNode.getFlatten())
return spv::SelectionControlFlattenMask;
if (selectionNode.getDontFlatten())
return spv::SelectionControlDontFlattenMask;
return spv::SelectionControlMaskNone;
}
spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(glslang::TLoopControl loopControl) const
spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSwitchControl(const glslang::TIntermSwitch& switchNode) 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;
}
if (switchNode.getFlatten())
return spv::SelectionControlFlattenMask;
if (switchNode.getDontFlatten())
return spv::SelectionControlDontFlattenMask;
return spv::SelectionControlMaskNone;
}
spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode) const
{
spv::LoopControlMask control = spv::LoopControlMaskNone;
if (loopNode.getDontUnroll())
control = control | spv::LoopControlDontUnrollMask;
if (loopNode.getUnroll())
control = control | spv::LoopControlUnrollMask;
return control;
}
// Translate glslang type to SPIR-V storage class.
@ -2026,7 +2035,7 @@ bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang
node->getCondition()->traverse(this);
// Selection control:
const spv::SelectionControlMask control = TranslateSelectionControl(node->getSelectionControl());
const spv::SelectionControlMask control = TranslateSelectionControl(*node);
// make an "if" based on the value created by the condition
spv::Builder::If ifBuilder(accessChainLoad(node->getCondition()->getType()), control, builder);
@ -2068,7 +2077,7 @@ bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::T
spv::Id selector = accessChainLoad(node->getCondition()->getAsTyped()->getType());
// Selection control:
const spv::SelectionControlMask control = TranslateSelectionControl(node->getSelectionControl());
const spv::SelectionControlMask control = TranslateSwitchControl(*node);
// browse the children to sort out code segments
int defaultSegment = -1;
@ -2128,7 +2137,7 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn
builder.createBranch(&blocks.head);
// Loop control:
const spv::LoopControlMask control = TranslateLoopControl(node->getLoopControl());
const spv::LoopControlMask control = TranslateLoopControl(*node);
// TODO: dependency length

View File

@ -1,20 +1,20 @@
hlsl.numthreads.comp
Shader version: 500
local_size = (4, 4, 2)
local_size = (1, 4, 8)
0:? Sequence
0:4 Function Definition: main(vu3; ( temp void)
0:4 Function Parameters:
0:4 'tid' ( in 3-component vector of uint)
0:9 Function Definition: @main_aux1(vu3; ( temp void)
0:9 Function Definition: @main_aux2(vu3; ( temp void)
0:9 Function Parameters:
0:9 'tid' ( in 3-component vector of uint)
0:9 Function Definition: main_aux1( ( temp void)
0:9 Function Definition: main_aux2( ( temp void)
0:9 Function Parameters:
0:? Sequence
0:9 move second child to first child ( temp 3-component vector of uint)
0:? 'tid' ( temp 3-component vector of uint)
0:? 'tid' ( in 3-component vector of uint GlobalInvocationID)
0:9 Function Call: @main_aux1(vu3; ( temp void)
0:9 Function Call: @main_aux2(vu3; ( temp void)
0:? 'tid' ( temp 3-component vector of uint)
0:? Linker Objects
0:? 'tid' ( in 3-component vector of uint GlobalInvocationID)
@ -24,21 +24,21 @@ Linked compute stage:
Shader version: 500
local_size = (4, 4, 2)
local_size = (1, 4, 8)
0:? Sequence
0:4 Function Definition: main(vu3; ( temp void)
0:4 Function Parameters:
0:4 'tid' ( in 3-component vector of uint)
0:9 Function Definition: @main_aux1(vu3; ( temp void)
0:9 Function Definition: @main_aux2(vu3; ( temp void)
0:9 Function Parameters:
0:9 'tid' ( in 3-component vector of uint)
0:9 Function Definition: main_aux1( ( temp void)
0:9 Function Definition: main_aux2( ( temp void)
0:9 Function Parameters:
0:? Sequence
0:9 move second child to first child ( temp 3-component vector of uint)
0:? 'tid' ( temp 3-component vector of uint)
0:? 'tid' ( in 3-component vector of uint GlobalInvocationID)
0:9 Function Call: @main_aux1(vu3; ( temp void)
0:9 Function Call: @main_aux2(vu3; ( temp void)
0:? 'tid' ( temp 3-component vector of uint)
0:? Linker Objects
0:? 'tid' ( in 3-component vector of uint GlobalInvocationID)
@ -50,13 +50,13 @@ local_size = (4, 4, 2)
Capability Shader
1: ExtInstImport "GLSL.std.450"
MemoryModel Logical GLSL450
EntryPoint GLCompute 4 "main_aux1" 18
ExecutionMode 4 LocalSize 4 4 2
EntryPoint GLCompute 4 "main_aux2" 18
ExecutionMode 4 LocalSize 1 4 8
Source HLSL 500
Name 4 "main_aux1"
Name 4 "main_aux2"
Name 11 "main(vu3;"
Name 10 "tid"
Name 14 "@main_aux1(vu3;"
Name 14 "@main_aux2(vu3;"
Name 13 "tid"
Name 16 "tid"
Name 18 "tid"
@ -70,7 +70,7 @@ local_size = (4, 4, 2)
9: TypeFunction 2 8(ptr)
17: TypePointer Input 7(ivec3)
18(tid): 17(ptr) Variable Input
4(main_aux1): 2 Function None 3
4(main_aux2): 2 Function None 3
5: Label
16(tid): 8(ptr) Variable Function
20(param): 8(ptr) Variable Function
@ -78,7 +78,7 @@ local_size = (4, 4, 2)
Store 16(tid) 19
21: 7(ivec3) Load 16(tid)
Store 20(param) 21
22: 2 FunctionCall 14(@main_aux1(vu3;) 20(param)
22: 2 FunctionCall 14(@main_aux2(vu3;) 20(param)
Return
FunctionEnd
11(main(vu3;): 2 Function None 9
@ -86,7 +86,7 @@ local_size = (4, 4, 2)
12: Label
Return
FunctionEnd
14(@main_aux1(vu3;): 2 Function None 9
14(@main_aux2(vu3;): 2 Function None 9
13(tid): 8(ptr) FunctionParameter
15: Label
Return

View File

@ -4,11 +4,8 @@ void main(uint3 tid : SV_DispatchThreadID )
{
}
[numTHreaDs(4,4,2)] // case insensitive
void main_aux1(uint3 tid : SV_DispatchThreadID )
[numthreads(1,4,8)]
void main_aux2(uint3 tid : SV_DispatchThreadID )
{
}
[numthreads(1,4,8)]
void main_aux2(uint3 tid : SV_DispatchThreadID );

View File

@ -9,6 +9,7 @@ endif(WIN32)
set(SOURCES
MachineIndependent/glslang.y
MachineIndependent/glslang_tab.cpp
MachineIndependent/attribute.cpp
MachineIndependent/Constant.cpp
MachineIndependent/iomapper.cpp
MachineIndependent/InfoSink.cpp
@ -51,6 +52,7 @@ set(HEADERS
Include/revision.h
Include/ShHandle.h
Include/Types.h
MachineIndependent/attribute.h
MachineIndependent/glslang_tab.cpp.h
MachineIndependent/gl_types.h
MachineIndependent/Initialize.h

View File

@ -37,6 +37,9 @@
#ifndef _CONSTANT_UNION_INCLUDED_
#define _CONSTANT_UNION_INCLUDED_
#include "../Include/Common.h"
#include "../Include/BaseTypes.h"
namespace glslang {
class TConstUnion {

View File

@ -819,7 +819,7 @@ public:
virtual glslang::TIntermMethod* getAsMethodNode() { return 0; }
virtual glslang::TIntermSymbol* getAsSymbolNode() { return 0; }
virtual glslang::TIntermBranch* getAsBranchNode() { return 0; }
virtual glslang::TIntermLoop* getAsLoopNode() { return 0; }
virtual glslang::TIntermLoop* getAsLoopNode() { return 0; }
virtual const glslang::TIntermTyped* getAsTyped() const { return 0; }
virtual const glslang::TIntermOperator* getAsOperator() const { return 0; }
@ -832,7 +832,7 @@ public:
virtual const glslang::TIntermMethod* getAsMethodNode() const { return 0; }
virtual const glslang::TIntermSymbol* getAsSymbolNode() const { return 0; }
virtual const glslang::TIntermBranch* getAsBranchNode() const { return 0; }
virtual const glslang::TIntermLoop* getAsLoopNode() const { return 0; }
virtual const glslang::TIntermLoop* getAsLoopNode() const { return 0; }
virtual ~TIntermNode() { }
protected:
@ -885,24 +885,6 @@ protected:
TType type;
};
//
// Selection control hints
//
enum TSelectionControl {
ESelectionControlNone,
ESelectionControlFlatten,
ESelectionControlDontFlatten,
};
//
// Loop control hints
//
enum TLoopControl {
ELoopControlNone,
ELoopControlUnroll,
ELoopControlDontUnroll,
};
//
// Handle for, do-while, and while loops.
//
@ -913,26 +895,30 @@ public:
test(aTest),
terminal(aTerminal),
first(testFirst),
control(ELoopControlNone)
unroll(false),
dontUnroll(false)
{ }
virtual TIntermLoop* getAsLoopNode() { return this; }
virtual const TIntermLoop* getAsLoopNode() const { return this; }
virtual TIntermLoop* getAsLoopNode() { return this; }
virtual const TIntermLoop* getAsLoopNode() const { return this; }
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; }
void setUnroll() { unroll = true; }
void setDontUnroll() { dontUnroll = true; }
bool getUnroll() const { return unroll; }
bool getDontUnroll() const { return dontUnroll; }
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
bool unroll; // true if unroll requested
bool dontUnroll; // true if request to not unroll
};
//
@ -1343,22 +1329,29 @@ protected:
class TIntermSelection : public TIntermTyped {
public:
TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB) :
TIntermTyped(EbtVoid), condition(cond), trueBlock(trueB), falseBlock(falseB), control(ESelectionControlNone) {}
TIntermTyped(EbtVoid), condition(cond), trueBlock(trueB), falseBlock(falseB),
flatten(false), dontFlatten(false) {}
TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB, const TType& type) :
TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB), control(ESelectionControlNone) {}
TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB),
flatten(false), dontFlatten(false) {}
virtual void traverse(TIntermTraverser*);
virtual TIntermTyped* getCondition() const { return condition; }
virtual TIntermNode* getTrueBlock() const { return trueBlock; }
virtual TIntermNode* getFalseBlock() const { return falseBlock; }
virtual TIntermSelection* getAsSelectionNode() { return this; }
virtual const TIntermSelection* getAsSelectionNode() const { return this; }
void setSelectionControl(TSelectionControl c) { control = c; }
TSelectionControl getSelectionControl() const { return control; }
void setFlatten() { flatten = true; }
void setDontFlatten() { dontFlatten = true; }
bool getFlatten() const { return flatten; }
bool getDontFlatten() const { return dontFlatten; }
protected:
TIntermTyped* condition;
TIntermNode* trueBlock;
TIntermNode* falseBlock;
TSelectionControl control; // selection control hint
bool flatten; // true if flatten requested
bool dontFlatten; // true if requested to not flatten
};
//
@ -1369,18 +1362,24 @@ protected:
//
class TIntermSwitch : public TIntermNode {
public:
TIntermSwitch(TIntermTyped* cond, TIntermAggregate* b) : condition(cond), body(b), control(ESelectionControlNone) { }
TIntermSwitch(TIntermTyped* cond, TIntermAggregate* b) : condition(cond), body(b),
flatten(false), dontFlatten(false) {}
virtual void traverse(TIntermTraverser*);
virtual TIntermNode* getCondition() const { return condition; }
virtual TIntermAggregate* getBody() const { return body; }
virtual TIntermSwitch* getAsSwitchNode() { return this; }
virtual const TIntermSwitch* getAsSwitchNode() const { return this; }
void setSelectionControl(TSelectionControl c) { control = c; }
TSelectionControl getSelectionControl() const { return control; }
void setFlatten() { flatten = true; }
void setDontFlatten() { dontFlatten = true; }
bool getFlatten() const { return flatten; }
bool getDontFlatten() const { return dontFlatten; }
protected:
TIntermTyped* condition;
TIntermAggregate* body;
TSelectionControl control; // selection control hint
bool flatten; // true if flatten requested
bool dontFlatten; // true if requested to not flatten
};
enum TVisit

View File

@ -1614,7 +1614,7 @@ TIntermAggregate* TIntermediate::makeAggregate(const TSourceLoc& loc)
//
// Returns the selection node created.
//
TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc, TSelectionControl control)
TIntermSelection* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc)
{
//
// Don't prune the false path for compile-time constants; it's needed
@ -1623,7 +1623,6 @@ TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair no
TIntermSelection* node = new TIntermSelection(cond, nodePair.node1, nodePair.node2);
node->setLoc(loc);
node->setSelectionControl(control);
return node;
}
@ -1666,12 +1665,13 @@ TIntermTyped* TIntermediate::addMethod(TIntermTyped* object, const TType& type,
//
// Returns the selection node created, or nullptr if one could not be.
//
TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc& loc, TSelectionControl control)
TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock,
const TSourceLoc& loc)
{
// If it's void, go to the if-then-else selection()
if (trueBlock->getBasicType() == EbtVoid && falseBlock->getBasicType() == EbtVoid) {
TIntermNodePair pair = { trueBlock, falseBlock };
return addSelection(cond, pair, loc, control);
return addSelection(cond, pair, loc);
}
//
@ -1909,11 +1909,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, TLoopControl control)
TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst,
const TSourceLoc& loc)
{
TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst);
node->setLoc(loc);
node->setLoopControl(control);
return node;
}
@ -1921,11 +1921,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, TLoopControl control)
TIntermAggregate* TIntermediate::addForLoop(TIntermNode* body, TIntermNode* initializer, TIntermTyped* test,
TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TIntermLoop*& node)
{
TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst);
node = new TIntermLoop(body, test, terminal, testFirst);
node->setLoc(loc);
node->setLoopControl(control);
// make a sequence of the initializer and statement, but try to reuse the
// aggregate already created for whatever is in the initializer, if there is one

View File

@ -0,0 +1,86 @@
//
// Copyright (C) 2017 LunarG, Inc.
// Copyright (C) 2018 Google, Inc.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of Google, Inc., nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
#include "attribute.h"
#include "../Include/intermediate.h"
#include "ParseHelper.h"
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);
if (intConst == nullptr)
return false;
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);
if (stringConst == nullptr)
return false;
value = *stringConst->getSConst();
// Convenience.
if (convertToLower)
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
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;
const TConstUnion* constVal = &args->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0];
if (constVal == nullptr || constVal->getType() != basicType)
return nullptr;
return constVal;
}
} // end namespace glslang

View File

@ -0,0 +1,97 @@
//
// Copyright (C) 2017 LunarG, Inc.
// Copyright (C) 2018 Google, Inc.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
#ifndef _ATTRIBUTE_INCLUDED_
#define _ATTRIBUTE_INCLUDED_
#include "../Include/Common.h"
#include "../Include/ConstantUnion.h"
namespace glslang {
enum TAttributeType {
EatNone,
EatAllow_uav_condition,
EatBranch,
EatCall,
EatDomain,
EatEarlyDepthStencil,
EatFastOpt,
EatFlatten,
EatForceCase,
EatInstance,
EatMaxTessFactor,
EatNumThreads,
EatMaxVertexCount,
EatOutputControlPoints,
EatOutputTopology,
EatPartitioning,
EatPatchConstantFunc,
EatPatchSize,
EatUnroll,
EatLoop,
EatBinding,
EatGlobalBinding,
EatLocation,
EatInputAttachment,
EatBuiltIn,
EatPushConstant,
EatConstantId
};
class TIntermAggregate;
struct TAttributeArgs {
TAttributeType name;
TIntermAggregate* args;
// Obtain attribute as integer
// Return false if it cannot be obtained
bool getInt(int& value, int argNum = 0) const;
// Obtain attribute as string, with optional to-lower transform
// Return false if it cannot be obtained
bool getString(TString& value, int argNum = 0, bool convertToLower = true) const;
protected:
const TConstUnion* getConstUnion(TBasicType basicType, int argNum) const;
};
typedef TList<TAttributeArgs> TAttributes;
} // end namespace glslang
#endif // _ATTRIBUTE_INCLUDED_

View File

@ -420,8 +420,8 @@ public:
TIntermAggregate* makeAggregate(const TSourceLoc&);
TIntermTyped* setAggregateOperator(TIntermNode*, TOperator, const TType& type, TSourceLoc);
bool areAllChildConst(TIntermAggregate* aggrNode);
TIntermTyped* addSelection(TIntermTyped* cond, TIntermNodePair code, const TSourceLoc&, TSelectionControl = ESelectionControlNone);
TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc&, TSelectionControl = ESelectionControlNone);
TIntermSelection* addSelection(TIntermTyped* cond, TIntermNodePair code, const TSourceLoc&);
TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc&);
TIntermTyped* addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc&);
TIntermTyped* addMethod(TIntermTyped*, const TType&, const TString*, const TSourceLoc&);
TIntermConstantUnion* addConstantUnion(const TConstUnionArray&, const TType&, const TSourceLoc&, bool literal = false) const;
@ -439,8 +439,9 @@ 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&, TLoopControl = ELoopControlNone);
TIntermAggregate* addForLoop(TIntermNode*, TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&, TLoopControl = ELoopControlNone);
TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&);
TIntermAggregate* addForLoop(TIntermNode*, TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst,
const TSourceLoc&, TIntermLoop*&);
TIntermBranch* addBranch(TOperator, const TSourceLoc&);
TIntermBranch* addBranch(TOperator, TIntermTyped*, const TSourceLoc&);
template<typename selectorType> TIntermTyped* addSwizzle(TSwizzleSelectors<selectorType>&, const TSourceLoc&);

View File

@ -247,7 +247,7 @@ INSTANTIATE_TEST_CASE_P(
{"hlsl.matrixindex.frag", "main"},
{"hlsl.nonstaticMemberFunction.frag", "main"},
{"hlsl.numericsuffixes.frag", "main"},
{"hlsl.numthreads.comp", "main_aux1"},
{"hlsl.numthreads.comp", "main_aux2"},
{"hlsl.overload.frag", "PixelShaderFunction"},
{"hlsl.opaque-type-bug.frag", "main"},
{"hlsl.params.default.frag", "main"},

View File

@ -34,157 +34,73 @@
//
#include "hlslAttributes.h"
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include "hlslParseHelper.h"
namespace glslang {
// Map the given string to an attribute enum from TAttributeType,
// or EatNone if invalid.
TAttributeType TAttributeMap::attributeFromName(const TString& nameSpace, const TString& name)
TAttributeType HlslParseContext::attributeFromName(const TString& nameSpace, const TString& name) const
{
// These are case insensitive.
TString lowername(name);
std::transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower);
TString lowernameSpace(nameSpace);
std::transform(lowernameSpace.begin(), lowernameSpace.end(), lowernameSpace.begin(), ::tolower);
// handle names within a namespace
if (lowernameSpace == "vk") {
if (lowername == "input_attachment_index")
if (nameSpace == "vk") {
if (name == "input_attachment_index")
return EatInputAttachment;
else if (lowername == "location")
else if (name == "location")
return EatLocation;
else if (lowername == "binding")
else if (name == "binding")
return EatBinding;
else if (lowername == "global_cbuffer_binding")
else if (name == "global_cbuffer_binding")
return EatGlobalBinding;
else if (lowername == "builtin")
else if (name == "builtin")
return EatBuiltIn;
else if (lowername == "constant_id")
else if (name == "constant_id")
return EatConstantId;
else if (lowername == "push_constant")
else if (name == "push_constant")
return EatPushConstant;
} else if (lowernameSpace.size() > 0)
} else if (nameSpace.size() > 0)
return EatNone;
// handle names with no namespace
if (lowername == "allow_uav_condition")
if (name == "allow_uav_condition")
return EatAllow_uav_condition;
else if (lowername == "branch")
else if (name == "branch")
return EatBranch;
else if (lowername == "call")
else if (name == "call")
return EatCall;
else if (lowername == "domain")
else if (name == "domain")
return EatDomain;
else if (lowername == "earlydepthstencil")
else if (name == "earlydepthstencil")
return EatEarlyDepthStencil;
else if (lowername == "fastopt")
else if (name == "fastopt")
return EatFastOpt;
else if (lowername == "flatten")
else if (name == "flatten")
return EatFlatten;
else if (lowername == "forcecase")
else if (name == "forcecase")
return EatForceCase;
else if (lowername == "instance")
else if (name == "instance")
return EatInstance;
else if (lowername == "maxtessfactor")
else if (name == "maxtessfactor")
return EatMaxTessFactor;
else if (lowername == "maxvertexcount")
else if (name == "maxvertexcount")
return EatMaxVertexCount;
else if (lowername == "numthreads")
else if (name == "numthreads")
return EatNumThreads;
else if (lowername == "outputcontrolpoints")
else if (name == "outputcontrolpoints")
return EatOutputControlPoints;
else if (lowername == "outputtopology")
else if (name == "outputtopology")
return EatOutputTopology;
else if (lowername == "partitioning")
else if (name == "partitioning")
return EatPartitioning;
else if (lowername == "patchconstantfunc")
else if (name == "patchconstantfunc")
return EatPatchConstantFunc;
else if (lowername == "unroll")
else if (name == "unroll")
return EatUnroll;
else if (lowername == "loop")
else if (name == "loop")
return EatLoop;
else
return EatNone;
}
// Look up entry, inserting if it's not there, and if name is a valid attribute name
// as known by attributeFromName.
TAttributeType TAttributeMap::setAttribute(const TString& nameSpace, const TString* name, TIntermAggregate* value)
{
if (name == nullptr)
return EatNone;
const TAttributeType attr = attributeFromName(nameSpace, *name);
if (attr != EatNone)
attributes[attr] = value;
return attr;
}
// Look up entry (const version), and return aggregate node. This cannot change the map.
const TIntermAggregate* TAttributeMap::operator[](TAttributeType attr) const
{
const auto entry = attributes.find(attr);
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();
}
// extract integers out of attribute arguments stored in attribute aggregate
bool TAttributeMap::getInt(TAttributeType attr, int& value, int argNum) const
{
const TConstUnion* intConst = getConstUnion(attr, EbtInt, argNum);
if (intConst == nullptr)
return false;
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 TAttributeMap::getString(TAttributeType attr, TString& value, int argNum, bool convertToLower) const
{
const TConstUnion* stringConst = getConstUnion(attr, EbtString, argNum);
if (stringConst == nullptr)
return false;
value = *stringConst->getSConst();
// Convenience.
if (convertToLower)
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
return true;
};
// Helper to get attribute const union. Returns nullptr on failure.
const TConstUnion* TAttributeMap::getConstUnion(TAttributeType attr, TBasicType basicType, int argNum) const
{
const TIntermAggregate* attrAgg = (*this)[attr];
if (attrAgg == nullptr)
return nullptr;
if (argNum >= int(attrAgg->getSequence().size()))
return nullptr;
const TConstUnion* constVal = &attrAgg->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0];
if (constVal == nullptr || constVal->getType() != basicType)
return nullptr;
return constVal;
}
} // end namespace glslang

View File

@ -38,93 +38,22 @@
#include <unordered_map>
#include <functional>
#include "../glslang/MachineIndependent/attribute.h"
#include "../glslang/MachineIndependent/SymbolTable.h"
#include "hlslScanContext.h"
#include "../glslang/Include/Common.h"
namespace glslang {
enum TAttributeType {
EatNone,
EatAllow_uav_condition,
EatBranch,
EatCall,
EatDomain,
EatEarlyDepthStencil,
EatFastOpt,
EatFlatten,
EatForceCase,
EatInstance,
EatMaxTessFactor,
EatNumThreads,
EatMaxVertexCount,
EatOutputControlPoints,
EatOutputTopology,
EatPartitioning,
EatPatchConstantFunc,
EatPatchSize,
EatUnroll,
EatLoop,
EatBinding,
EatGlobalBinding,
EatLocation,
EatInputAttachment,
EatBuiltIn,
EatPushConstant,
EatConstantId
};
}
namespace std {
// Allow use of TAttributeType enum in hash_map without calling code having to cast.
template <> struct hash<glslang::TAttributeType> {
std::size_t operator()(glslang::TAttributeType attr) const {
return std::hash<int>()(int(attr));
}
};
} // end namespace std
namespace glslang {
class TIntermAggregate;
class TAttributeMap {
public:
int size() const { return (int)attributes.size(); }
// Search for and potentially add the attribute into the map. Return the
// attribute type enum for it, if found, else EatNone.
TAttributeType setAttribute(const TString& nameSpace, const TString* name, TIntermAggregate* value);
// 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;
// Obtain attribute as integer
bool getInt(TAttributeType attr, int& value, int argNum = 0) const;
// Obtain attribute as string, with optional to-lower transform
bool getString(TAttributeType attr, TString& value, int argNum = 0, bool convertToLower = true) const;
protected:
// Helper to get attribute const union
const TConstUnion* getConstUnion(TAttributeType attr, TBasicType, int argNum) const;
// Find an attribute enum given its name.
static TAttributeType attributeFromName(const TString& nameSpace, const TString& name);
std::unordered_map<TAttributeType, TIntermAggregate*> attributes;
};
class TFunctionDeclarator {
public:
TFunctionDeclarator() : function(nullptr), body(nullptr) { }
TSourceLoc loc;
TFunction* function;
TAttributeMap attributes;
TAttributes attributes;
TVector<HlslToken>* body;
};
} // end namespace glslang
#endif // HLSLATTRIBUTES_H_

View File

@ -396,6 +396,9 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
if (peekTokenClass(EHTokLeftParen)) {
// looks like function parameters
// merge in the attributes into the return type
parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType, true);
// Potentially rename shader entry point function. No-op most of the time.
parseContext.renameShaderFunction(fullName);
@ -423,7 +426,13 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
parseContext.handleFunctionDeclarator(declarator.loc, *declarator.function, true);
}
} else {
// A variable declaration. Fix the storage qualifier if it's a global.
// A variable declaration.
// merge in the attributes, the first time around, into the shared type
if (! declarator_list)
parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType);
// Fix the storage qualifier if it's a global.
if (declaredType.getQualifier().storage == EvqTemporary && parseContext.symbolTable.atGlobalLevel())
declaredType.getQualifier().storage = EvqUniform;
@ -536,13 +545,16 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
bool HlslGrammar::acceptControlDeclaration(TIntermNode*& node)
{
node = nullptr;
TAttributeMap attributes;
TAttributes attributes;
// fully_specified_type
TType type;
if (! acceptFullySpecifiedType(type, attributes))
return false;
if (attributes.size() > 0)
parseContext.warn(token.loc, "attributes don't apply to control declaration", "", "");
// filter out type casts
if (peekTokenClass(EHTokLeftParen)) {
recedeToken();
@ -578,12 +590,12 @@ bool HlslGrammar::acceptControlDeclaration(TIntermNode*& node)
// : type_specifier
// | type_qualifier type_specifier
//
bool HlslGrammar::acceptFullySpecifiedType(TType& type, const TAttributeMap& attributes)
bool HlslGrammar::acceptFullySpecifiedType(TType& type, const TAttributes& attributes)
{
TIntermNode* nodeList = nullptr;
return acceptFullySpecifiedType(type, nodeList, attributes);
}
bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList, const TAttributeMap& attributes, bool forbidDeclarators)
bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList, const TAttributes& attributes, bool forbidDeclarators)
{
// type_qualifier
TQualifier qualifier;
@ -608,7 +620,7 @@ bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList,
parseContext.mergeQualifiers(type.getQualifier(), qualifier);
// merge in the attributes
parseContext.transferTypeAttributes(attributes, type);
parseContext.transferTypeAttributes(token.loc, attributes, type);
// further, it can create an anonymous instance of the block
// (cbuffer and tbuffer don't consume the next identifier, and
@ -633,9 +645,6 @@ bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList,
qualifier.builtIn = type.getQualifier().builtIn;
type.getQualifier() = qualifier;
// merge in the attributes
parseContext.transferTypeAttributes(attributes, type);
}
return true;
@ -2335,7 +2344,7 @@ bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList, TIntermNode*
// struct_declaration
// attributes
TAttributeMap attributes;
TAttributes attributes;
acceptAttributes(attributes);
bool declarator_list = false;
@ -2347,6 +2356,9 @@ bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList, TIntermNode*
return false;
}
// merge in the attributes
parseContext.transferTypeAttributes(token.loc, attributes, memberType);
// struct_declarator COMMA struct_declarator ...
bool functionDefinitionAccepted = false;
do {
@ -2542,7 +2554,7 @@ bool HlslGrammar::acceptDefaultParameterDeclaration(const TType& type, TIntermTy
bool HlslGrammar::acceptParameterDeclaration(TFunction& function)
{
// attributes
TAttributeMap attributes;
TAttributes attributes;
acceptAttributes(attributes);
// fully_specified_type
@ -2550,6 +2562,9 @@ bool HlslGrammar::acceptParameterDeclaration(TFunction& function)
if (! acceptFullySpecifiedType(*type, attributes))
return false;
// merge in the attributes
parseContext.transferTypeAttributes(token.loc, attributes, *type);
// identifier
HlslToken idToken;
acceptIdentifier(idToken);
@ -3386,7 +3401,7 @@ bool HlslGrammar::acceptStatement(TIntermNode*& statement)
statement = nullptr;
// attributes
TAttributeMap attributes;
TAttributes attributes;
acceptAttributes(attributes);
// attributed_statement
@ -3458,7 +3473,7 @@ bool HlslGrammar::acceptStatement(TIntermNode*& statement)
// | PATCHCONSTANTFUNC
// | NUMTHREADS LEFT_PAREN x_size, y_size,z z_size RIGHT_PAREN
//
void HlslGrammar::acceptAttributes(TAttributeMap& attributes)
void HlslGrammar::acceptAttributes(TAttributes& attributes)
{
// For now, accept the [ XXX(X) ] syntax, but drop all but
// numthreads, which is used to set the CS local size.
@ -3529,9 +3544,16 @@ void HlslGrammar::acceptAttributes(TAttributeMap& attributes)
return;
}
// Add any values we found into the attribute map. This accepts
// (and ignores) values not mapping to a known TAttributeType;
attributes.setAttribute(nameSpace, attributeToken.string, expressions);
// Add any values we found into the attribute map.
if (attributeToken.string != nullptr) {
TAttributeType attributeType = parseContext.attributeFromName(nameSpace, *attributeToken.string);
if (attributeType == EatNone)
parseContext.warn(attributeToken.loc, "unrecognized attribute", attributeToken.string->c_str(), "");
else {
TAttributeArgs attributeArgs = { attributeType, expressions };
attributes.push_back(attributeArgs);
}
}
} while (true);
}
@ -3539,12 +3561,10 @@ void HlslGrammar::acceptAttributes(TAttributeMap& attributes)
// : IF LEFT_PAREN expression RIGHT_PAREN statement
// : IF LEFT_PAREN expression RIGHT_PAREN statement ELSE statement
//
bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttributeMap& attributes)
bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttributes& attributes)
{
TSourceLoc loc = token.loc;
const TSelectionControl control = parseContext.handleSelectionControl(attributes);
// IF
if (! acceptTokenClass(EHTokIf))
return false;
@ -3582,7 +3602,9 @@ bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttri
}
// Put the pieces together
statement = intermediate.addSelection(condition, thenElse, loc, control);
statement = intermediate.addSelection(condition, thenElse, loc);
parseContext.handleSelectionAttributes(loc, statement->getAsSelectionNode(), attributes);
parseContext.popScope();
--parseContext.controlFlowNestingLevel;
@ -3592,13 +3614,11 @@ bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttri
// switch_statement
// : SWITCH LEFT_PAREN expression RIGHT_PAREN compound_statement
//
bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttributeMap& attributes)
bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttributes& attributes)
{
// SWITCH
TSourceLoc loc = token.loc;
const TSelectionControl control = parseContext.handleSelectionControl(attributes);
if (! acceptTokenClass(EHTokSwitch))
return false;
@ -3618,7 +3638,8 @@ bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttribut
--parseContext.controlFlowNestingLevel;
if (statementOkay)
statement = parseContext.addSwitch(loc, switchExpression, statement ? statement->getAsAggregate() : nullptr, control);
statement = parseContext.addSwitch(loc, switchExpression, statement ? statement->getAsAggregate() : nullptr,
attributes);
parseContext.popSwitchSequence();
parseContext.popScope();
@ -3632,7 +3653,7 @@ bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttribut
// | 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, const TAttributeMap& attributes)
bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttributes& attributes)
{
TSourceLoc loc = token.loc;
TIntermTyped* condition = nullptr;
@ -3643,8 +3664,7 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttri
// WHILE or DO or FOR
advanceToken();
const TLoopControl control = parseContext.handleLoopControl(attributes);
TIntermLoop* loopNode = nullptr;
switch (loop) {
case EHTokWhile:
// so that something declared in the condition is scoped to the lifetime
@ -3670,9 +3690,9 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttri
parseContext.popScope();
--parseContext.controlFlowNestingLevel;
statement = intermediate.addLoop(statement, condition, nullptr, true, loc, control);
return true;
loopNode = intermediate.addLoop(statement, condition, nullptr, true, loc);
statement = loopNode;
break;
case EHTokDo:
parseContext.nestLooping(); // this only needs to work right if no errors
@ -3703,9 +3723,9 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttri
parseContext.unnestLooping();
--parseContext.controlFlowNestingLevel;
statement = intermediate.addLoop(statement, condition, 0, false, loc, control);
return true;
loopNode = intermediate.addLoop(statement, condition, 0, false, loc);
statement = loopNode;
break;
case EHTokFor:
{
@ -3747,18 +3767,21 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttri
return false;
}
statement = intermediate.addForLoop(statement, initNode, condition, iterator, true, loc, control);
statement = intermediate.addForLoop(statement, initNode, condition, iterator, true, loc, loopNode);
parseContext.popScope();
parseContext.unnestLooping();
--parseContext.controlFlowNestingLevel;
return true;
break;
}
default:
return false;
}
parseContext.handleLoopAttributes(loc, loopNode, attributes);
return true;
}
// jump_statement

View File

@ -43,7 +43,6 @@
namespace glslang {
class TAttributeMap;
class TFunctionDeclarator;
// Should just be the grammar aspect of HLSL.
@ -71,8 +70,8 @@ namespace glslang {
bool acceptControlDeclaration(TIntermNode*& node);
bool acceptSamplerDeclarationDX9(TType&);
bool acceptSamplerState();
bool acceptFullySpecifiedType(TType&, const TAttributeMap&);
bool acceptFullySpecifiedType(TType&, TIntermNode*& nodeList, const TAttributeMap&, bool forbidDeclarators = false);
bool acceptFullySpecifiedType(TType&, const TAttributes&);
bool acceptFullySpecifiedType(TType&, TIntermNode*& nodeList, const TAttributes&, bool forbidDeclarators = false);
bool acceptQualifier(TQualifier&);
bool acceptLayoutQualifierList(TQualifier&);
bool acceptType(TType&);
@ -117,10 +116,10 @@ namespace glslang {
bool acceptScopedCompoundStatement(TIntermNode*&);
bool acceptStatement(TIntermNode*&);
bool acceptNestedStatement(TIntermNode*&);
void acceptAttributes(TAttributeMap&);
bool acceptSelectionStatement(TIntermNode*&, const TAttributeMap&);
bool acceptSwitchStatement(TIntermNode*&, const TAttributeMap&);
bool acceptIterationStatement(TIntermNode*&, const TAttributeMap&);
void acceptAttributes(TAttributes&);
bool acceptSelectionStatement(TIntermNode*&, const TAttributes&);
bool acceptSwitchStatement(TIntermNode*&, const TAttributes&);
bool acceptIterationStatement(TIntermNode*&, const TAttributes&);
bool acceptJumpStatement(TIntermNode*&);
bool acceptCaseLabel(TIntermNode*&);
bool acceptDefaultLabel(TIntermNode*&);

View File

@ -1620,7 +1620,7 @@ void HlslParseContext::addStructBufferHiddenCounterParam(const TSourceLoc& loc,
// Returns an aggregate of parameter-symbol nodes.
//
TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function,
const TAttributeMap& attributes,
const TAttributes& attributes,
TIntermNode*& entryPointTree)
{
currentCaller = function.getMangledName();
@ -1717,189 +1717,217 @@ TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& l
}
// Handle all [attrib] attribute for the shader entry point
void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributeMap& attributes)
void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributes& attributes)
{
// Handle entry-point function attributes
const TIntermAggregate* numThreads = attributes[EatNumThreads];
if (numThreads != nullptr) {
const TIntermSequence& sequence = numThreads->getSequence();
for (int lid = 0; lid < int(sequence.size()); ++lid)
intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
}
// MaxVertexCount
if (attributes.contains(EatMaxVertexCount)) {
int maxVertexCount;
if (! attributes.getInt(EatMaxVertexCount, maxVertexCount)) {
error(loc, "invalid maxvertexcount", "", "");
} else {
if (! intermediate.setVertices(maxVertexCount))
error(loc, "cannot change previously set maxvertexcount attribute", "", "");
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
switch (it->name) {
case EatNumThreads:
{
const TIntermSequence& sequence = it->args->getSequence();
for (int lid = 0; lid < int(sequence.size()); ++lid)
intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
break;
}
}
case EatMaxVertexCount:
{
int maxVertexCount;
// Handle [patchconstantfunction("...")]
if (attributes.contains(EatPatchConstantFunc)) {
TString pcfName;
if (! attributes.getString(EatPatchConstantFunc, pcfName, 0, false)) {
error(loc, "invalid patch constant function", "", "");
} else {
patchConstantFunctionName = pcfName;
if (! it->getInt(maxVertexCount)) {
error(loc, "invalid maxvertexcount", "", "");
} else {
if (! intermediate.setVertices(maxVertexCount))
error(loc, "cannot change previously set maxvertexcount attribute", "", "");
}
break;
}
}
// Handle [domain("...")]
if (attributes.contains(EatDomain)) {
TString domainStr;
if (! attributes.getString(EatDomain, domainStr)) {
error(loc, "invalid domain", "", "");
} else {
TLayoutGeometry domain = ElgNone;
if (domainStr == "tri") {
domain = ElgTriangles;
} else if (domainStr == "quad") {
domain = ElgQuads;
} else if (domainStr == "isoline") {
domain = ElgIsolines;
case EatPatchConstantFunc:
{
TString pcfName;
if (! it->getString(pcfName, 0, false)) {
error(loc, "invalid patch constant function", "", "");
} else {
error(loc, "unsupported domain type", domainStr.c_str(), "");
}
if (language == EShLangTessEvaluation) {
if (! intermediate.setInputPrimitive(domain))
error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
} else {
if (! intermediate.setOutputPrimitive(domain))
error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
patchConstantFunctionName = pcfName;
}
break;
}
}
// Handle [outputtopology("...")]
if (attributes.contains(EatOutputTopology)) {
TString topologyStr;
if (! attributes.getString(EatOutputTopology, topologyStr)) {
error(loc, "invalid outputtopology", "", "");
} else {
TVertexOrder vertexOrder = EvoNone;
TLayoutGeometry primitive = ElgNone;
if (topologyStr == "point") {
intermediate.setPointMode();
} else if (topologyStr == "line") {
primitive = ElgIsolines;
} else if (topologyStr == "triangle_cw") {
vertexOrder = EvoCw;
primitive = ElgTriangles;
} else if (topologyStr == "triangle_ccw") {
vertexOrder = EvoCcw;
primitive = ElgTriangles;
case EatDomain:
{
// Handle [domain("...")]
TString domainStr;
if (! it->getString(domainStr)) {
error(loc, "invalid domain", "", "");
} else {
error(loc, "unsupported outputtopology type", topologyStr.c_str(), "");
}
TLayoutGeometry domain = ElgNone;
if (vertexOrder != EvoNone) {
if (! intermediate.setVertexOrder(vertexOrder)) {
error(loc, "cannot change previously set outputtopology",
TQualifier::getVertexOrderString(vertexOrder), "");
if (domainStr == "tri") {
domain = ElgTriangles;
} else if (domainStr == "quad") {
domain = ElgQuads;
} else if (domainStr == "isoline") {
domain = ElgIsolines;
} else {
error(loc, "unsupported domain type", domainStr.c_str(), "");
}
if (language == EShLangTessEvaluation) {
if (! intermediate.setInputPrimitive(domain))
error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
} else {
if (! intermediate.setOutputPrimitive(domain))
error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
}
}
if (primitive != ElgNone)
intermediate.setOutputPrimitive(primitive);
break;
}
}
// Handle [partitioning("...")]
if (attributes.contains(EatPartitioning)) {
TString partitionStr;
if (! attributes.getString(EatPartitioning, partitionStr)) {
error(loc, "invalid partitioning", "", "");
} else {
TVertexSpacing partitioning = EvsNone;
if (partitionStr == "integer") {
partitioning = EvsEqual;
} else if (partitionStr == "fractional_even") {
partitioning = EvsFractionalEven;
} else if (partitionStr == "fractional_odd") {
partitioning = EvsFractionalOdd;
//} else if (partition == "pow2") { // TODO: currently nothing to map this to.
case EatOutputTopology:
{
// Handle [outputtopology("...")]
TString topologyStr;
if (! it->getString(topologyStr)) {
error(loc, "invalid outputtopology", "", "");
} else {
error(loc, "unsupported partitioning type", partitionStr.c_str(), "");
}
TVertexOrder vertexOrder = EvoNone;
TLayoutGeometry primitive = ElgNone;
if (! intermediate.setVertexSpacing(partitioning))
error(loc, "cannot change previously set partitioning",
TQualifier::getVertexSpacingString(partitioning), "");
if (topologyStr == "point") {
intermediate.setPointMode();
} else if (topologyStr == "line") {
primitive = ElgIsolines;
} else if (topologyStr == "triangle_cw") {
vertexOrder = EvoCw;
primitive = ElgTriangles;
} else if (topologyStr == "triangle_ccw") {
vertexOrder = EvoCcw;
primitive = ElgTriangles;
} else {
error(loc, "unsupported outputtopology type", topologyStr.c_str(), "");
}
if (vertexOrder != EvoNone) {
if (! intermediate.setVertexOrder(vertexOrder)) {
error(loc, "cannot change previously set outputtopology",
TQualifier::getVertexOrderString(vertexOrder), "");
}
}
if (primitive != ElgNone)
intermediate.setOutputPrimitive(primitive);
}
break;
}
}
case EatPartitioning:
{
// Handle [partitioning("...")]
TString partitionStr;
if (! it->getString(partitionStr)) {
error(loc, "invalid partitioning", "", "");
} else {
TVertexSpacing partitioning = EvsNone;
// Handle [outputcontrolpoints("...")]
if (attributes.contains(EatOutputControlPoints)) {
int ctrlPoints;
if (! attributes.getInt(EatOutputControlPoints, ctrlPoints)) {
error(loc, "invalid outputcontrolpoints", "", "");
} else {
if (! intermediate.setVertices(ctrlPoints)) {
error(loc, "cannot change previously set outputcontrolpoints attribute", "", "");
if (partitionStr == "integer") {
partitioning = EvsEqual;
} else if (partitionStr == "fractional_even") {
partitioning = EvsFractionalEven;
} else if (partitionStr == "fractional_odd") {
partitioning = EvsFractionalOdd;
//} else if (partition == "pow2") { // TODO: currently nothing to map this to.
} else {
error(loc, "unsupported partitioning type", partitionStr.c_str(), "");
}
if (! intermediate.setVertexSpacing(partitioning))
error(loc, "cannot change previously set partitioning",
TQualifier::getVertexSpacingString(partitioning), "");
}
break;
}
case EatOutputControlPoints:
{
// Handle [outputcontrolpoints("...")]
int ctrlPoints;
if (! it->getInt(ctrlPoints)) {
error(loc, "invalid outputcontrolpoints", "", "");
} else {
if (! intermediate.setVertices(ctrlPoints)) {
error(loc, "cannot change previously set outputcontrolpoints attribute", "", "");
}
}
break;
}
case EatBuiltIn:
case EatLocation:
// tolerate these because of dual use of entrypoint and type attributes
break;
default:
warn(loc, "attribute does not apply to entry point", "", "");
break;
}
}
}
// Update the given type with any type-like attribute information in the
// attributes.
void HlslParseContext::transferTypeAttributes(const TAttributeMap& attributes, TType& type)
void HlslParseContext::transferTypeAttributes(const TSourceLoc& loc, const TAttributes& attributes, TType& type,
bool allowEntry)
{
if (attributes.size() == 0)
return;
// location
int value;
if (attributes.getInt(EatLocation, value))
type.getQualifier().layoutLocation = value;
// binding
if (attributes.getInt(EatBinding, value)) {
type.getQualifier().layoutBinding = value;
type.getQualifier().layoutSet = 0;
}
// set
if (attributes.getInt(EatBinding, value, 1))
type.getQualifier().layoutSet = value;
// global cbuffer binding
if (attributes.getInt(EatGlobalBinding, value))
globalUniformBinding = value;
// global cbuffer binding
if (attributes.getInt(EatGlobalBinding, value, 1))
globalUniformSet = value;
// input attachment
if (attributes.getInt(EatInputAttachment, value))
type.getQualifier().layoutAttachment = value;
// PointSize built-in
TString builtInString;
if (attributes.getString(EatBuiltIn, builtInString, 0, false)) {
if (builtInString == "PointSize")
type.getQualifier().builtIn = EbvPointSize;
}
// push_constant
if (attributes.contains(EatPushConstant))
type.getQualifier().layoutPushConstant = true;
// specialization constant
if (attributes.getInt(EatConstantId, value)) {
TSourceLoc loc;
loc.init();
setSpecConstantId(loc, type.getQualifier(), value);
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
switch (it->name) {
case EatLocation:
// location
if (it->getInt(value))
type.getQualifier().layoutLocation = value;
break;
case EatBinding:
// binding
if (it->getInt(value)) {
type.getQualifier().layoutBinding = value;
type.getQualifier().layoutSet = 0;
}
// set
if (it->getInt(value, 1))
type.getQualifier().layoutSet = value;
break;
case EatGlobalBinding:
// global cbuffer binding
if (it->getInt(value))
globalUniformBinding = value;
// global cbuffer binding
if (it->getInt(value, 1))
globalUniformSet = value;
break;
case EatInputAttachment:
// input attachment
if (it->getInt(value))
type.getQualifier().layoutAttachment = value;
break;
case EatBuiltIn:
// PointSize built-in
if (it->getString(builtInString, 0, false)) {
if (builtInString == "PointSize")
type.getQualifier().builtIn = EbvPointSize;
}
break;
case EatPushConstant:
// push_constant
type.getQualifier().layoutPushConstant = true;
break;
case EatConstantId:
// specialization constant
if (it->getInt(value)) {
TSourceLoc loc;
loc.init();
setSpecConstantId(loc, type.getQualifier(), value);
}
break;
default:
if (! allowEntry)
warn(loc, "attribute does not apply to a type", "", "");
break;
}
}
}
@ -1936,7 +1964,7 @@ void HlslParseContext::transferTypeAttributes(const TAttributeMap& attributes, T
// a subtree that creates the entry point.
//
TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction,
const TAttributeMap& attributes)
const TAttributes& attributes)
{
// Return true if this is a tessellation patch constant function input to a domain shader.
const auto isDsPcfInput = [this](const TType& type) {
@ -8792,29 +8820,75 @@ bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayout
}
//
// Selection hints
// Selection attributes
//
TSelectionControl HlslParseContext::handleSelectionControl(const TAttributeMap& attributes) const
void HlslParseContext::handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection* selection,
const TAttributes& attributes)
{
if (attributes.contains(EatFlatten))
return ESelectionControlFlatten;
else if (attributes.contains(EatBranch))
return ESelectionControlDontFlatten;
else
return ESelectionControlNone;
if (selection == nullptr)
return;
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
switch (it->name) {
case EatFlatten:
selection->setFlatten();
break;
case EatBranch:
selection->setDontFlatten();
break;
default:
warn(loc, "attribute does not apply to a selection", "", "");
break;
}
}
}
//
// Switch attributes
//
void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch* selection,
const TAttributes& attributes)
{
if (selection == nullptr)
return;
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
switch (it->name) {
case EatFlatten:
selection->setFlatten();
break;
case EatBranch:
selection->setDontFlatten();
break;
default:
warn(loc, "attribute does not apply to a switch", "", "");
break;
}
}
}
//
// Loop hints
//
TLoopControl HlslParseContext::handleLoopControl(const TAttributeMap& attributes) const
void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop,
const TAttributes& attributes)
{
if (attributes.contains(EatUnroll))
return ELoopControlUnroll;
else if (attributes.contains(EatLoop))
return ELoopControlDontUnroll;
else
return ELoopControlNone;
if (loop == nullptr)
return;
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
switch (it->name) {
case EatUnroll:
loop->setUnroll();
break;
case EatLoop:
loop->setDontUnroll();
break;
default:
warn(loc, "attribute does not apply to a loop", "", "");
break;
}
}
}
//
@ -8959,7 +9033,7 @@ void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIn
// into a switch node.
//
TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression,
TIntermAggregate* lastStatements, TSelectionControl control)
TIntermAggregate* lastStatements, const TAttributes& attributes)
{
wrapupSwitchSubsequence(lastStatements, nullptr);
@ -8986,7 +9060,7 @@ TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* ex
TIntermSwitch* switchNode = new TIntermSwitch(expression, body);
switchNode->setLoc(loc);
switchNode->setSelectionControl(control);
handleSwitchAttributes(loc, switchNode, attributes);
return switchNode;
}

View File

@ -38,12 +38,12 @@
#include "../glslang/MachineIndependent/parseVersions.h"
#include "../glslang/MachineIndependent/ParseHelper.h"
#include "../glslang/MachineIndependent/attribute.h"
#include <array>
namespace glslang {
class TAttributeMap; // forward declare
class TFunctionDeclarator;
class HlslParseContext : public TParseContextBase {
@ -80,10 +80,10 @@ public:
bool isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field);
void assignToInterface(TVariable& variable);
void handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype);
TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&, const TAttributeMap&, TIntermNode*& entryPointTree);
TIntermNode* transformEntryPoint(const TSourceLoc&, TFunction&, const TAttributeMap&);
void handleEntryPointAttributes(const TSourceLoc&, const TAttributeMap&);
void transferTypeAttributes(const TAttributeMap&, TType&);
TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&, const TAttributes&, TIntermNode*& entryPointTree);
TIntermNode* transformEntryPoint(const TSourceLoc&, TFunction&, const TAttributes&);
void handleEntryPointAttributes(const TSourceLoc&, const TAttributes&);
void transferTypeAttributes(const TSourceLoc&, const TAttributes&, TType&, bool allowEntry = false);
void handleFunctionBody(const TSourceLoc&, TFunction&, TIntermNode* functionBody, TIntermNode*& node);
void remapEntryPointIO(TFunction& function, TVariable*& returnValue, TVector<TVariable*>& inputs, TVector<TVariable*>& outputs);
void remapNonEntryPointIO(TFunction& function);
@ -163,7 +163,7 @@ public:
void addQualifierToExisting(const TSourceLoc&, TQualifier, TIdentifierList&);
void updateStandaloneQualifierDefaults(const TSourceLoc&, const TPublicType&);
void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode);
TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body, TSelectionControl control);
TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body, const TAttributes&);
void updateImplicitArraySize(const TSourceLoc&, TIntermNode*, int index);
@ -203,10 +203,11 @@ public:
bool handleInputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry);
// Determine selection control from attributes
TSelectionControl handleSelectionControl(const TAttributeMap& attributes) const;
void handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection*, const TAttributes& attributes);
void handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch*, const TAttributes& attributes);
// Determine loop control from attributes
TLoopControl handleLoopControl(const TAttributeMap& attributes) const;
void handleLoopAttributes(const TSourceLoc& loc, TIntermLoop*, const TAttributes& attributes);
// Share struct buffer deep types
void shareStructBufferType(TType&);
@ -217,6 +218,8 @@ public:
// Obtain the sampler return type of the given sampler in retType.
void getTextureReturnType(const TSampler& sampler, TType& retType) const;
TAttributeType attributeFromName(const TString& nameSpace, const TString& name) const;
protected:
struct TFlattenData {
TFlattenData() : nextBinding(TQualifier::layoutBindingEnd),