From 0840838d1739f2d5ebfe2037f4c6f09478710689 Mon Sep 17 00:00:00 2001
From: qining <qining@google.com>
Date: Mon, 21 Mar 2016 09:51:37 -0400
Subject: [PATCH] Support specialization composite constants

Fix issue #163, support creation and reference of composite type
specialization constants.

e.g.:
```
layout(constant_id = 200) const float myfloat = 1.25;
layout(constant_id = 201) const int myint = 14;
struct structtype {
  float f;
  int i;
};
const structtype outer_struct_var = {myfloat, myint};
void main(){}
```
generated code (use glslangValidator):
```
// Module Version 10000
// Generated by (magic number): 80001
// Id's are bound by 12

                              Capability Shader
               1:             ExtInstImport  "GLSL.std.450"
                              MemoryModel Logical GLSL450
                              EntryPoint Vertex 4  "main"
                              Source GLSL 450
                              Name 4  "main"
                              Name 10  "structtype"
                              MemberName 10(structtype) 0  "f"
                              MemberName 10(structtype) 1  "i"
                              Decorate 7 SpecId 200
                              Decorate 9 SpecId 201
               2:             TypeVoid
               3:             TypeFunction 2
               6:             TypeFloat 32
               7:    6(float) SpecConstant 1067450368
               8:             TypeInt 32 1
               9:      8(int) SpecConstant 14
  10(structtype):             TypeStruct 6(float) 8(int)
              11:10(structtype) SpecConstantComposite 7 9
         4(main):           2 Function None 3
               5:             Label
                              Return
                              FunctionEnd
```

Rname two function names to match their functionalities.
1) Rename `GlslangToSpvTraverser::createSpvSpecConstant()` to
`createSpvConstant()`;
2) Rename `GlslangToSpvTraverser::createSpvConstant()` to
`createSpvConstantFromConstUnionArray()`

Add function `GlslangToSpvTraverser::createSpvConstantFromSubTree()` to
handle constant creation from sub trees (e.g.: specialization constants).

Related PR: #208
---
 SPIRV/GlslangToSpv.cpp                        |  87 +++++++--
 .../spv.specConstantComposite.vert.out        | 172 ++++++++++++++++++
 Test/spv.specConstantComposite.vert           |  93 ++++++++++
 Test/test-spirv-list                          |   1 +
 glslang/Include/Types.h                       |   1 +
 glslang/MachineIndependent/ParseHelper.cpp    |  27 ++-
 6 files changed, 362 insertions(+), 19 deletions(-)
 create mode 100644 Test/baseResults/spv.specConstantComposite.vert.out
 create mode 100644 Test/spv.specConstantComposite.vert

diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index 6f5697e3db..dd5afd4b66 100755
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -128,8 +128,9 @@ class TGlslangToSpvTraverser : public glslang::TIntermTraverser {
     void addDecoration(spv::Id id, spv::Decoration dec, unsigned value);
     void addMemberDecoration(spv::Id id, int member, spv::Decoration dec);
     void addMemberDecoration(spv::Id id, int member, spv::Decoration dec, unsigned value);
-    spv::Id createSpvSpecConstant(const glslang::TIntermTyped&);
-    spv::Id createSpvConstant(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst, bool specConstant);
+    spv::Id createSpvConstant(const glslang::TIntermTyped&);
+    spv::Id createSpvConstantFromConstUnionArray(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst, bool specConstant);
+    spv::Id createSpvConstantFromConstSubTree(const glslang::TIntermTyped* subTree);
     bool isTrivialLeaf(const glslang::TIntermTyped* node);
     bool isTrivial(const glslang::TIntermTyped* node);
     spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right);
@@ -1520,7 +1521,7 @@ bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::T
 void TGlslangToSpvTraverser::visitConstantUnion(glslang::TIntermConstantUnion* node)
 {
     int nextConst = 0;
-    spv::Id constant = createSpvConstant(node->getType(), node->getConstArray(), nextConst, false);
+    spv::Id constant = createSpvConstantFromConstUnionArray(node->getType(), node->getConstArray(), nextConst, false);
 
     builder.clearAccessChain();
     builder.setAccessChainRValue(constant);
@@ -1630,7 +1631,7 @@ spv::Id TGlslangToSpvTraverser::createSpvVariable(const glslang::TIntermSymbol*
     // can still have a mapping to a SPIR-V Id.
     // This includes specialization constants.
     if (node->getQualifier().isConstant()) {
-        return createSpvSpecConstant(*node);
+        return createSpvConstant(*node);
     }
 
     // Now, handle actual variables
@@ -3730,7 +3731,7 @@ void TGlslangToSpvTraverser::addMemberDecoration(spv::Id id, int member, spv::De
 // recursively walks.  So, this function walks the "top" of the tree:
 //  - emit specialization constant-building instructions for specConstant
 //  - when running into a non-spec-constant, switch to createSpvConstant()
-spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermTyped& node)
+spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TIntermTyped& node)
 {
     assert(node.getQualifier().isConstant());
 
@@ -3738,7 +3739,7 @@ spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermType
         // hand off to the non-spec-constant path
         assert(node.getAsConstantUnion() != nullptr || node.getAsSymbolNode() != nullptr);
         int nextConst = 0;
-        return createSpvConstant(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(),
+        return createSpvConstantFromConstUnionArray(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(),
                                  nextConst, false);
     }
 
@@ -3747,7 +3748,7 @@ spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermType
     if (node.getAsSymbolNode() && node.getQualifier().hasSpecConstantId()) {
         // this is a direct literal assigned to a layout(constant_id=) declaration
         int nextConst = 0;
-        return createSpvConstant(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(),
+        return createSpvConstantFromConstUnionArray(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(),
                                  nextConst, true);
     } else {
         // gl_WorkgroupSize is a special case until the front-end handles hierarchical specialization constants,
@@ -3761,8 +3762,10 @@ spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermType
                     addDecoration(dimConstId.back(), spv::DecorationSpecId, glslangIntermediate->getLocalSizeSpecId(dim));
             }
             return builder.makeCompositeConstant(builder.makeVectorType(builder.makeUintType(32), 3), dimConstId, true);
+        } else if (auto* sn = node.getAsSymbolNode()){
+            return createSpvConstantFromConstSubTree(sn->getConstSubtree());
         } else {
-            spv::MissingFunctionality("specialization-constant expression trees");
+            spv::MissingFunctionality("Neither a front-end constant nor a spec constant.");
             exit(1);
             return spv::NoResult;
         }
@@ -3775,7 +3778,7 @@ spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermType
 // If there are not enough elements present in 'consts', 0 will be substituted;
 // an empty 'consts' can be used to create a fully zeroed SPIR-V constant.
 //
-spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangType, const glslang::TConstUnionArray& consts, int& nextConst, bool specConstant)
+spv::Id TGlslangToSpvTraverser::createSpvConstantFromConstUnionArray(const glslang::TType& glslangType, const glslang::TConstUnionArray& consts, int& nextConst, bool specConstant)
 {
     // vector of constants for SPIR-V
     std::vector<spv::Id> spvConsts;
@@ -3786,15 +3789,15 @@ spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangT
     if (glslangType.isArray()) {
         glslang::TType elementType(glslangType, 0);
         for (int i = 0; i < glslangType.getOuterArraySize(); ++i)
-            spvConsts.push_back(createSpvConstant(elementType, consts, nextConst, false));
+            spvConsts.push_back(createSpvConstantFromConstUnionArray(elementType, consts, nextConst, false));
     } else if (glslangType.isMatrix()) {
         glslang::TType vectorType(glslangType, 0);
         for (int col = 0; col < glslangType.getMatrixCols(); ++col)
-            spvConsts.push_back(createSpvConstant(vectorType, consts, nextConst, false));
+            spvConsts.push_back(createSpvConstantFromConstUnionArray(vectorType, consts, nextConst, false));
     } else if (glslangType.getStruct()) {
         glslang::TVector<glslang::TTypeLoc>::const_iterator iter;
         for (iter = glslangType.getStruct()->begin(); iter != glslangType.getStruct()->end(); ++iter)
-            spvConsts.push_back(createSpvConstant(*iter->type, consts, nextConst, false));
+            spvConsts.push_back(createSpvConstantFromConstUnionArray(*iter->type, consts, nextConst, false));
     } else if (glslangType.isVector()) {
         for (unsigned int i = 0; i < (unsigned int)glslangType.getVectorSize(); ++i) {
             bool zero = nextConst >= consts.size();
@@ -3851,6 +3854,66 @@ spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangT
     return builder.makeCompositeConstant(typeId, spvConsts);
 }
 
+// Create constant ID from const initializer sub tree.
+spv::Id TGlslangToSpvTraverser::createSpvConstantFromConstSubTree(
+    const glslang::TIntermTyped* subTree) {
+    const glslang::TType& glslangType = subTree->getType();
+    spv::Id typeId = convertGlslangToSpvType(glslangType);
+    bool is_spec_const = subTree->getType().getQualifier().isSpecConstant();
+    if (const glslang::TIntermAggregate* an = subTree->getAsAggregate()) {
+        // Aggregate node, we should generate OpConstantComposite or
+        // OpSpecConstantComposite instruction.
+        std::vector<spv::Id> const_constituents;
+        for (auto NI = an->getSequence().begin(); NI != an->getSequence().end();
+             NI++) {
+            const_constituents.push_back(
+                createSpvConstantFromConstSubTree((*NI)->getAsTyped()));
+        }
+        // Note that constructors are aggregate nodes, so expressions like:
+        // float x = float(y) will become an aggregate node. If 'x' is declared
+        // as a constant, the aggregate node representing 'float(y)' will be
+        // processed here.
+        if (builder.isVectorType(typeId) || builder.isMatrixType(typeId) ||
+            builder.isAggregateType(typeId)) {
+            return builder.makeCompositeConstant(typeId, const_constituents, is_spec_const);
+        } else {
+            assert(builder.isScalarType(typeId) && const_constituents.size() == 1);
+            return const_constituents.front();
+        }
+
+    } else if (const glslang::TIntermBinary* bn = subTree->getAsBinaryNode()) {
+        // Binary operation node, we should generate OpSpecConstantOp <binary op>
+        // This case should only happen when Specialization Constants are involved.
+        spv::MissingFunctionality("OpSpecConstantOp <binary op> not implemented");
+        return spv::NoResult;
+
+    } else if (const glslang::TIntermUnary* un = subTree->getAsUnaryNode()) {
+        // Unary operation node, similar to binary operation node, should only
+        // happen when specialization constants are involved.
+        spv::MissingFunctionality("OpSpecConstantOp <unary op> not implemented");
+        return spv::NoResult;
+
+    } else if (const glslang::TIntermConstantUnion* cn = subTree->getAsConstantUnion()) {
+        // ConstantUnion node, should redirect to
+        // createSpvConstantFromConstUnionArray
+        int nextConst = 0;
+        return createSpvConstantFromConstUnionArray(
+            glslangType, cn->getConstArray(), nextConst, is_spec_const);
+
+    } else if (const glslang::TIntermSymbol* sn = subTree->getAsSymbolNode()) {
+        // Symbol node. Call getSymbolId(). This should cover both cases 1) the
+        // symbol has already been assigned an ID, 2) need a new ID for this
+        // symbol.
+        return getSymbolId(sn);
+
+    } else {
+        spv::MissingFunctionality(
+            "createSpvConstantFromConstSubTree() not covered TIntermTyped* const "
+            "initializer subtree.");
+        return spv::NoResult;
+    }
+}
+
 // Return true if the node is a constant or symbol whose reading has no
 // non-trivial observable cost or effect.
 bool TGlslangToSpvTraverser::isTrivialLeaf(const glslang::TIntermTyped* node)
diff --git a/Test/baseResults/spv.specConstantComposite.vert.out b/Test/baseResults/spv.specConstantComposite.vert.out
new file mode 100644
index 0000000000..5e2dfa4a76
--- /dev/null
+++ b/Test/baseResults/spv.specConstantComposite.vert.out
@@ -0,0 +1,172 @@
+spv.specConstantComposite.vert
+Warning, version 450 is not yet complete; most version-specific features are present, but some are missing.
+
+
+Linked vertex stage:
+
+
+// Module Version 10000
+// Generated by (magic number): 80001
+// Id's are bound by 106
+
+                              Capability Shader
+                              Capability Float64
+               1:             ExtInstImport  "GLSL.std.450"
+                              MemoryModel Logical GLSL450
+                              EntryPoint Vertex 4  "main" 27 105
+                              Source GLSL 450
+                              Name 4  "main"
+                              Name 6  "refer_primary_spec_const("
+                              Name 8  "refer_composite_spec_const("
+                              Name 10  "refer_copmosite_dot_dereference("
+                              Name 12  "refer_composite_bracket_dereference("
+                              Name 16  "refer_spec_const_array_length("
+                              Name 18  "declare_spec_const_in_func("
+                              Name 27  "color"
+                              Name 41  "flat_struct"
+                              MemberName 41(flat_struct) 0  "i"
+                              MemberName 41(flat_struct) 1  "f"
+                              MemberName 41(flat_struct) 2  "d"
+                              MemberName 41(flat_struct) 3  "b"
+                              Name 42  "nesting_struct"
+                              MemberName 42(nesting_struct) 0  "nested"
+                              MemberName 42(nesting_struct) 1  "v"
+                              MemberName 42(nesting_struct) 2  "i"
+                              Name 72  "indexable"
+                              Name 76  "indexable"
+                              Name 83  "len"
+                              Name 105  "global_vec4_array_with_spec_length"
+                              Decorate 21 SpecId 203
+                              Decorate 28 SpecId 200
+                              Decorate 32 SpecId 201
+                              Decorate 43 SpecId 202
+               2:             TypeVoid
+               3:             TypeFunction 2
+              14:             TypeInt 32 1
+              15:             TypeFunction 14(int)
+              20:             TypeBool
+              21:    20(bool) SpecConstantTrue
+              24:             TypeFloat 32
+              25:             TypeVector 24(float) 4
+              26:             TypePointer Output 25(fvec4)
+       27(color):     26(ptr) Variable Output
+              28:     14(int) SpecConstant 3
+              32:   24(float) SpecConstant 1078523331
+              33:   25(fvec4) SpecConstantComposite 32 32 32 32
+              36:   24(float) Constant 1133908460
+              37:   25(fvec4) SpecConstantComposite 32 32 36 36
+              40:             TypeFloat 64
+ 41(flat_struct):             TypeStruct 14(int) 24(float) 40(float) 20(bool)
+42(nesting_struct):             TypeStruct 41(flat_struct) 25(fvec4) 14(int)
+              43:   40(float) SpecConstant 1413754136 1074340347
+              44:41(flat_struct) SpecConstantComposite 28 32 43 21
+              45:42(nesting_struct) SpecConstantComposite 44 33 28
+              46:     14(int) Constant 2
+              51:             TypeInt 32 0
+              52:     51(int) Constant 0
+              57:     51(int) Constant 5
+              58:             TypeArray 24(float) 57
+              59:   24(float) Constant 1065353216
+              60:   24(float) Constant 1073741824
+              61:   24(float) Constant 1077936128
+              62:          58 SpecConstantComposite 32 32 59 60 61
+              63:     14(int) Constant 1
+              68:             TypeArray 14(int) 57
+              69:     14(int) Constant 30
+              70:          68 SpecConstantComposite 28 28 63 46 69
+              71:             TypePointer Function 68
+              73:             TypePointer Function 14(int)
+              87:   24(float) Constant 1106321080
+              88:41(flat_struct) SpecConstantComposite 69 87 43 21
+              89:     14(int) Constant 10
+              90:42(nesting_struct) SpecConstantComposite 88 37 89
+              96:    20(bool) ConstantFalse
+              97:41(flat_struct) SpecConstantComposite 28 32 43 96
+              98:   24(float) Constant 1036831949
+              99:   25(fvec4) ConstantComposite 98 98 98 98
+             100:42(nesting_struct) SpecConstantComposite 97 99 28
+             101:     14(int) Constant 3000
+             102:42(nesting_struct) SpecConstantComposite 88 37 101
+             103:             TypeArray 25(fvec4) 28
+             104:             TypePointer Input 103
+105(global_vec4_array_with_spec_length):    104(ptr) Variable Input
+         4(main):           2 Function None 3
+               5:             Label
+                              Return
+                              FunctionEnd
+6(refer_primary_spec_const():           2 Function None 3
+               7:             Label
+                              SelectionMerge 23 None
+                              BranchConditional 21 22 23
+              22:               Label
+              29:   24(float)   ConvertSToF 28
+              30:   25(fvec4)   Load 27(color)
+              31:   25(fvec4)   VectorTimesScalar 30 29
+                                Store 27(color) 31
+                                Branch 23
+              23:             Label
+                              Return
+                              FunctionEnd
+8(refer_composite_spec_const():           2 Function None 3
+               9:             Label
+              34:   25(fvec4) Load 27(color)
+              35:   25(fvec4) FAdd 34 33
+                              Store 27(color) 35
+              38:   25(fvec4) Load 27(color)
+              39:   25(fvec4) FSub 38 37
+                              Store 27(color) 39
+                              Return
+                              FunctionEnd
+10(refer_copmosite_dot_dereference():           2 Function None 3
+              11:             Label
+              47:     14(int) CompositeExtract 45 2
+              48:   24(float) ConvertSToF 47
+              49:   25(fvec4) Load 27(color)
+              50:   25(fvec4) VectorTimesScalar 49 48
+                              Store 27(color) 50
+              53:   24(float) CompositeExtract 33 0
+              54:   25(fvec4) Load 27(color)
+              55:   25(fvec4) CompositeConstruct 53 53 53 53
+              56:   25(fvec4) FAdd 54 55
+                              Store 27(color) 56
+                              Return
+                              FunctionEnd
+12(refer_composite_bracket_dereference():           2 Function None 3
+              13:             Label
+   72(indexable):     71(ptr) Variable Function
+   76(indexable):     71(ptr) Variable Function
+              64:   24(float) CompositeExtract 62 1
+              65:   25(fvec4) Load 27(color)
+              66:   25(fvec4) CompositeConstruct 64 64 64 64
+              67:   25(fvec4) FSub 65 66
+                              Store 27(color) 67
+                              Store 72(indexable) 70
+              74:     73(ptr) AccessChain 72(indexable) 28
+              75:     14(int) Load 74
+                              Store 76(indexable) 70
+              77:     73(ptr) AccessChain 76(indexable) 75
+              78:     14(int) Load 77
+              79:   24(float) ConvertSToF 78
+              80:   25(fvec4) Load 27(color)
+              81:   25(fvec4) CompositeConstruct 79 79 79 79
+              82:   25(fvec4) FDiv 80 81
+                              Store 27(color) 82
+                              Return
+                              FunctionEnd
+16(refer_spec_const_array_length():     14(int) Function None 15
+              17:             Label
+         83(len):     73(ptr) Variable Function
+                              Store 83(len) 28
+              84:     14(int) Load 83(len)
+                              ReturnValue 84
+                              FunctionEnd
+18(declare_spec_const_in_func():           2 Function None 3
+              19:             Label
+              91:     14(int) CompositeExtract 90 2
+              92:   24(float) ConvertSToF 91
+              93:   25(fvec4) Load 27(color)
+              94:   25(fvec4) CompositeConstruct 92 92 92 92
+              95:   25(fvec4) FDiv 93 94
+                              Store 27(color) 95
+                              Return
+                              FunctionEnd
diff --git a/Test/spv.specConstantComposite.vert b/Test/spv.specConstantComposite.vert
new file mode 100644
index 0000000000..4450ddd668
--- /dev/null
+++ b/Test/spv.specConstantComposite.vert
@@ -0,0 +1,93 @@
+#version 450
+
+// constant_id specified scalar spec constants
+layout(constant_id = 200) const int spec_int = 3;
+layout(constant_id = 201) const float spec_float = 3.14;
+layout(constant_id = 202) const
+    double spec_double = 3.1415926535897932384626433832795;
+layout(constant_id = 203) const bool spec_bool = true;
+
+const float cast_spec_float = float(spec_float);
+
+// Flat struct
+struct flat_struct {
+    int i;
+    float f;
+    double d;
+    bool b;
+};
+
+// Nesting struct
+struct nesting_struct {
+    flat_struct nested;
+    vec4 v;
+    int i;
+};
+
+// Expect OpSpecConstantComposite
+// Flat struct initializer
+const flat_struct spec_flat_struct_all_spec = {spec_int, spec_float,
+                                               spec_double, spec_bool};
+const flat_struct spec_flat_struct_partial_spec = {30, 30.14, spec_double,
+                                                   spec_bool};
+
+// Nesting struct initializer
+const nesting_struct nesting_struct_ctor = {
+    {spec_int, spec_float, spec_double, false},
+    vec4(0.1, 0.1, 0.1, 0.1),
+    spec_int};
+
+// Vector constructor
+const vec4 spec_vec4_all_spec =
+    vec4(spec_float, spec_float, spec_float, spec_float);
+const vec4 spec_vec4_partial_spec =
+    vec4(spec_float, spec_float, 300.14, 300.14);
+
+// Struct nesting constructor
+const nesting_struct spec_nesting_struct_all_spec = {
+    spec_flat_struct_all_spec, spec_vec4_all_spec, spec_int};
+const nesting_struct spec_nesting_struct_partial_spec = {
+    spec_flat_struct_partial_spec, spec_vec4_partial_spec, 3000};
+
+const float spec_float_array[5] = {spec_float, spec_float, 1.0, 2.0, 3.0};
+const int spec_int_array[5] = {spec_int, spec_int, 1, 2, 30};
+
+// global_vec4_array_with_spec_length is not a spec constant, but its array
+// size is. When calling global_vec4_array_with_spec_length.length(), A
+// TIntermSymbol Node shoule be returned, instead of a TIntermConstantUnion
+// node which represents a known constant value.
+in vec4 global_vec4_array_with_spec_length[spec_int];
+
+out vec4 color;
+
+void refer_primary_spec_const() {
+    if (spec_bool) color *= spec_int;
+}
+
+void refer_composite_spec_const() {
+    color += spec_vec4_all_spec;
+    color -= spec_vec4_partial_spec;
+}
+
+void refer_copmosite_dot_dereference() {
+    color *= spec_nesting_struct_all_spec.i;
+    color += spec_vec4_all_spec.x;
+}
+
+void refer_composite_bracket_dereference() {
+    color -= spec_float_array[1];
+    color /= spec_int_array[spec_int_array[spec_int]];
+}
+
+int refer_spec_const_array_length() {
+    int len = global_vec4_array_with_spec_length.length();
+    return len;
+}
+
+void declare_spec_const_in_func() {
+    const nesting_struct spec_const_declared_in_func = {
+        spec_flat_struct_partial_spec, spec_vec4_partial_spec, 10};
+    color /= spec_const_declared_in_func.i;
+}
+
+void main() {}
diff --git a/Test/test-spirv-list b/Test/test-spirv-list
index 0e22367402..4534132f2b 100644
--- a/Test/test-spirv-list
+++ b/Test/test-spirv-list
@@ -102,6 +102,7 @@ spv.pushConstant.vert
 spv.subpass.frag
 spv.specConstant.vert
 spv.specConstant.comp
+spv.specConstantComposite.vert
 # GLSL-level semantics
 vulkan.frag
 vulkan.vert
diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h
index 05681c9a41..bff7e9a95f 100644
--- a/glslang/Include/Types.h
+++ b/glslang/Include/Types.h
@@ -1223,6 +1223,7 @@ class TType {
     virtual int getMatrixCols() const { return matrixCols; }
     virtual int getMatrixRows() const { return matrixRows; }
     virtual int getOuterArraySize()  const { return arraySizes->getOuterSize(); }
+    virtual TIntermTyped*  getOuterArrayNode() const { return arraySizes->getOuterNode(); }
     virtual int getCumulativeArraySize()  const { return arraySizes->getCumulativeSize(); }
     virtual bool isArrayOfArrays() const { return arraySizes != nullptr && arraySizes->getNumDims() > 1; }
     virtual int getImplicitArraySize() const { return arraySizes->getImplicitSize(); }
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index 132963183b..5de89e1b32 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -484,7 +484,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn
     TIntermTyped* result = nullptr;
 
     int indexValue = 0;
-    if (index->getQualifier().isConstant()) {
+    if (index->getQualifier().isFrontEndConstant()) {
         indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst();
         checkIndex(loc, base->getType(), indexValue);
     }
@@ -503,7 +503,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn
         if (base->getAsSymbolNode() && isIoResizeArray(base->getType()))
             handleIoResizeArrayAccess(loc, base);
 
-        if (index->getQualifier().isConstant()) {
+        if (index->getQualifier().isFrontEndConstant()) {
             if (base->getType().isImplicitlySizedArray())
                 updateImplicitArraySize(loc, base, indexValue);
             result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
@@ -541,10 +541,15 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn
     } else {
         // Insert valid dereferenced result
         TType newType(base->getType(), 0);  // dereferenced type
-        if (base->getType().getQualifier().isFrontEndConstant() && index->getQualifier().isFrontEndConstant())
+        if (base->getType().getQualifier().isConstant() && index->getQualifier().isConstant()) {
             newType.getQualifier().storage = EvqConst;
-        else
+            // If base or index is a specialization constant, the result should also be a specialization constant.
+            if (base->getType().getQualifier().isSpecConstant() || index->getQualifier().isSpecConstant()) {
+                newType.getQualifier().makeSpecConstant();
+            }
+        } else {
             newType.getQualifier().makePartialTemporary();
+        }
         result->setType(newType);
 
         if (anyIndexLimits)
@@ -1226,6 +1231,11 @@ TIntermTyped* TParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction
                     else
                         error(loc, "", function->getName().c_str(), "array must be declared with a size before using this method");
                 }
+            } else if (type.getOuterArrayNode()) {
+                // If the array's outer size is specified by an intermediate node, it means the array's length
+                // was specified by a specialization constant. In such a case, we should return the node of the
+                // specialization constants to represent the length.
+                return type.getOuterArrayNode();
             } else
                 length = type.getOuterArraySize();
         } else if (type.isMatrix())
@@ -5110,7 +5120,8 @@ TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode*
 
     // We don't know "top down" whether type is a specialization constant,
     // but a const becomes a specialization constant if any of its children are.
-    bool specConst = false;
+    bool hasSpecConst = false;
+    bool isConstConstrutor = true;
 
     for (TIntermSequence::iterator p = sequenceVector.begin();
                                    p != sequenceVector.end(); p++, paramCount++) {
@@ -5123,14 +5134,16 @@ TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode*
 
         if (newNode) {
             *p = newNode;
+            if (! newNode->getType().getQualifier().isConstant())
+                isConstConstrutor = false;
             if (newNode->getType().getQualifier().isSpecConstant())
-                specConst = true;
+                hasSpecConst = true;
         } else
             return nullptr;
     }
 
     TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc);
-    if (constructor->getType().getQualifier().isConstant() && specConst)
+    if (isConstConstrutor && hasSpecConst)
         constructor->getWritableType().getQualifier().makeSpecConstant();
 
     return constructor;