WIP: HLSL: support global const initializers from non-constant rvalues

Semantic test left over from other source languages is removed, since this is permitted by HLSL.
Also, to support the functionality, a targeted test is performed for this case and it is
turned into a EvqGlobal qualifier to create an AST initialization segment when needed.

Constness is now propagated up aggregate chains during initializer construction.  This
handles hierarchical cases such as the distinction between:

    static const float2 a[2] = { { 1, 2 }, { 3, 4} };

vs

    static const float2 a[2] = { { 1, 2 }, { cbuffer_member, 4} };

The first of which can use a first class constant initalization, and the second cannot.
This commit is contained in:
LoopDawg 2017-07-10 15:43:40 -06:00
parent 652db16ff1
commit 0fca0bafaf
5 changed files with 219 additions and 5 deletions

View File

@ -0,0 +1,178 @@
hlsl.global-const-init.frag
Shader version: 500
gl_FragCoord origin is upper left
0:? Sequence
0:6 Sequence
0:6 move second child to first child ( temp 4-component vector of float)
0:6 'bar' ( global 4-component vector of float)
0:6 foo: direct index for structure (layout( row_major std140) uniform 4-component vector of float)
0:6 'anon@0' (layout( row_major std140) uniform block{layout( row_major std140) uniform 4-component vector of float foo})
0:6 Constant:
0:6 0 (const uint)
0:8 Sequence
0:8 move second child to first child ( temp 2-element array of 2-component vector of float)
0:8 'a1' ( global 2-element array of 2-component vector of float)
0:8 Construct vec2 ( temp 2-element array of 2-component vector of float)
0:8 Constant:
0:8 1.000000
0:8 2.000000
0:8 Construct vec2 ( temp 2-component vector of float)
0:8 direct index ( temp float)
0:8 foo: direct index for structure (layout( row_major std140) uniform 4-component vector of float)
0:8 'anon@0' (layout( row_major std140) uniform block{layout( row_major std140) uniform 4-component vector of float foo})
0:8 Constant:
0:8 0 (const uint)
0:8 Constant:
0:8 0 (const int)
0:8 Constant:
0:8 4.000000
0:12 Function Definition: @main( ( temp 4-component vector of float)
0:12 Function Parameters:
0:? Sequence
0:13 Branch: Return with expression
0:13 'bar' ( global 4-component vector of float)
0:12 Function Definition: main( ( temp void)
0:12 Function Parameters:
0:? Sequence
0:12 move second child to first child ( temp 4-component vector of float)
0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float)
0:12 Function Call: @main( ( temp 4-component vector of float)
0:? Linker Objects
0:? 'anon@0' (layout( row_major std140) uniform block{layout( row_major std140) uniform 4-component vector of float foo})
0:? 'bar' ( global 4-component vector of float)
0:? 'a1' ( global 2-element array of 2-component vector of float)
0:? 'a2' ( const 2-element array of 2-component vector of float)
0:? 5.000000
0:? 6.000000
0:? 7.000000
0:? 8.000000
0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float)
Linked fragment stage:
Shader version: 500
gl_FragCoord origin is upper left
0:? Sequence
0:6 Sequence
0:6 move second child to first child ( temp 4-component vector of float)
0:6 'bar' ( global 4-component vector of float)
0:6 foo: direct index for structure (layout( row_major std140) uniform 4-component vector of float)
0:6 'anon@0' (layout( row_major std140) uniform block{layout( row_major std140) uniform 4-component vector of float foo})
0:6 Constant:
0:6 0 (const uint)
0:8 Sequence
0:8 move second child to first child ( temp 2-element array of 2-component vector of float)
0:8 'a1' ( global 2-element array of 2-component vector of float)
0:8 Construct vec2 ( temp 2-element array of 2-component vector of float)
0:8 Constant:
0:8 1.000000
0:8 2.000000
0:8 Construct vec2 ( temp 2-component vector of float)
0:8 direct index ( temp float)
0:8 foo: direct index for structure (layout( row_major std140) uniform 4-component vector of float)
0:8 'anon@0' (layout( row_major std140) uniform block{layout( row_major std140) uniform 4-component vector of float foo})
0:8 Constant:
0:8 0 (const uint)
0:8 Constant:
0:8 0 (const int)
0:8 Constant:
0:8 4.000000
0:12 Function Definition: @main( ( temp 4-component vector of float)
0:12 Function Parameters:
0:? Sequence
0:13 Branch: Return with expression
0:13 'bar' ( global 4-component vector of float)
0:12 Function Definition: main( ( temp void)
0:12 Function Parameters:
0:? Sequence
0:12 move second child to first child ( temp 4-component vector of float)
0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float)
0:12 Function Call: @main( ( temp 4-component vector of float)
0:? Linker Objects
0:? 'anon@0' (layout( row_major std140) uniform block{layout( row_major std140) uniform 4-component vector of float foo})
0:? 'bar' ( global 4-component vector of float)
0:? 'a1' ( global 2-element array of 2-component vector of float)
0:? 'a2' ( const 2-element array of 2-component vector of float)
0:? 5.000000
0:? 6.000000
0:? 7.000000
0:? 8.000000
0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float)
// Module Version 10000
// Generated by (magic number): 80001
// Id's are bound by 50
Capability Shader
1: ExtInstImport "GLSL.std.450"
MemoryModel Logical GLSL450
EntryPoint Fragment 4 "main" 41
ExecutionMode 4 OriginUpperLeft
Source HLSL 500
Name 4 "main"
Name 9 "@main("
Name 12 "bar"
Name 13 "CB"
MemberName 13(CB) 0 "foo"
Name 15 ""
Name 26 "a1"
Name 41 "@entryPointOutput"
MemberDecorate 13(CB) 0 Offset 0
Decorate 13(CB) Block
Decorate 15 DescriptorSet 0
Decorate 41(@entryPointOutput) Location 0
2: TypeVoid
3: TypeFunction 2
6: TypeFloat 32
7: TypeVector 6(float) 4
8: TypeFunction 7(fvec4)
11: TypePointer Private 7(fvec4)
12(bar): 11(ptr) Variable Private
13(CB): TypeStruct 7(fvec4)
14: TypePointer Uniform 13(CB)
15: 14(ptr) Variable Uniform
16: TypeInt 32 1
17: 16(int) Constant 0
18: TypePointer Uniform 7(fvec4)
21: TypeVector 6(float) 2
22: TypeInt 32 0
23: 22(int) Constant 2
24: TypeArray 21(fvec2) 23
25: TypePointer Private 24
26(a1): 25(ptr) Variable Private
27: 6(float) Constant 1065353216
28: 6(float) Constant 1073741824
29: 21(fvec2) ConstantComposite 27 28
30: 22(int) Constant 0
31: TypePointer Uniform 6(float)
34: 6(float) Constant 1082130432
40: TypePointer Output 7(fvec4)
41(@entryPointOutput): 40(ptr) Variable Output
43: 6(float) Constant 1084227584
44: 6(float) Constant 1086324736
45: 21(fvec2) ConstantComposite 43 44
46: 6(float) Constant 1088421888
47: 6(float) Constant 1090519040
48: 21(fvec2) ConstantComposite 46 47
49: 24 ConstantComposite 45 48
4(main): 2 Function None 3
5: Label
19: 18(ptr) AccessChain 15 17
20: 7(fvec4) Load 19
Store 12(bar) 20
32: 31(ptr) AccessChain 15 17 30
33: 6(float) Load 32
35: 21(fvec2) CompositeConstruct 33 34
36: 24 CompositeConstruct 29 35
Store 26(a1) 36
42: 7(fvec4) FunctionCall 9(@main()
Store 41(@entryPointOutput) 42
Return
FunctionEnd
9(@main(): 7(fvec4) Function None 8
10: Label
37: 7(fvec4) Load 12(bar)
ReturnValue 37
FunctionEnd

View File

@ -0,0 +1,14 @@
cbuffer CB {
float4 foo;
};
static const float4 bar = foo; // test const (in the immutable sense) initializer from non-const.
static const float2 a1[2] = { { 1, 2 }, { foo.x, 4 } }; // not entirely constant
static const float2 a2[2] = { { 5, 6 }, { 7, 8 } }; // entirely constant
float4 main() : SV_Target0
{
return bar;
}

View File

@ -137,6 +137,7 @@ INSTANTIATE_TEST_CASE_P(
{"hlsl.getdimensions.rw.dx10.frag", "main"},
{"hlsl.getdimensions.dx10.vert", "main"},
{"hlsl.getsampleposition.dx10.frag", "main"},
{"hlsl.global-const-init.frag", "main"},
{"hlsl.domain.1.tese", "main"},
{"hlsl.domain.2.tese", "main"},
{"hlsl.domain.3.tese", "main"},

View File

@ -2553,8 +2553,19 @@ bool HlslGrammar::acceptInitializer(TIntermTyped*& node)
expected("assignment expression in initializer list");
return false;
}
const bool firstNode = (node == nullptr);
node = intermediate.growAggregate(node, expr, loc);
// If every sub-node in the list has qualifier EvqConst, the returned node becomes
// EvqConst. Otherwise, it becomes EvqTemporary. That doesn't happen with e.g.
// EvqIn or EvqPosition, since the collection isn't EvqPosition if all the members are.
if (firstNode && expr->getQualifier().storage == EvqConst)
node->getQualifier().storage = EvqConst;
else if (expr->getQualifier().storage != EvqConst)
node->getQualifier().storage = EvqTemporary;
// COMMA
if (acceptTokenClass(EHTokComma)) {
if (acceptTokenClass(EHTokRightBrace)) // allow trailing comma

View File

@ -7165,6 +7165,21 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TStr
if (voidErrorCheck(loc, identifier, type.getBasicType()))
return nullptr;
// Global consts with initializers that are non-const act like EvqGlobal in HLSL.
// This test is implicitly recursive, because initializers propagate constness
// up the aggregate node tree during creation. E.g, for:
// { { 1, 2 }, { 3, 4 } }
// the initializer list is marked EvqConst at the top node, and remains so here. However:
// { 1, { myvar, 2 }, 3 }
// is not a const intializer, and still becomes EvqGlobal here.
const bool nonConstInitializer = (initializer != nullptr && initializer->getQualifier().storage != EvqConst);
if (type.getQualifier().storage == EvqConst && symbolTable.atGlobalLevel() && nonConstInitializer) {
// Force to global
type.getQualifier().storage = EvqGlobal;
}
// make const and initialization consistent
fixConstInit(loc, identifier, type, initializer);
@ -7345,11 +7360,6 @@ TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TInterm
variable->getWritableType().getQualifier().storage = EvqTemporary;
return nullptr;
}
if (qualifier == EvqConst && symbolTable.atGlobalLevel() && initializer->getType().getQualifier().storage != EvqConst) {
error(loc, "global const initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
variable->getWritableType().getQualifier().storage = EvqTemporary;
return nullptr;
}
// Const variables require a constant initializer
if (qualifier == EvqConst) {