HLSL: Add namespace grammar and some basic semantics.
authorJohn Kessenich <cepheus@frii.com>
Thu, 30 Mar 2017 22:22:26 +0000 (16:22 -0600)
committerJohn Kessenich <cepheus@frii.com>
Thu, 30 Mar 2017 22:30:17 +0000 (16:30 -0600)
Unknown how extensive the semantics need to be yet. Need real
feedback from workloads. This is just done as part of unifying it
with the class/struct namespaces and grammar productions.

Test/baseResults/hlsl.namespace.frag.out [new file with mode: 0755]
Test/hlsl.namespace.frag [new file with mode: 0755]
glslang/Include/revision.h
gtests/Hlsl.FromFile.cpp
hlsl/hlslGrammar.cpp
hlsl/hlslGrammar.h
hlsl/hlslScanContext.cpp
hlsl/hlslTokens.h

diff --git a/Test/baseResults/hlsl.namespace.frag.out b/Test/baseResults/hlsl.namespace.frag.out
new file mode 100755 (executable)
index 0000000..dae874f
--- /dev/null
@@ -0,0 +1,180 @@
+hlsl.namespace.frag
+Shader version: 450
+gl_FragCoord origin is upper left
+0:? Sequence
+0:5  Function Definition: N1::getVec( ( temp 4-component vector of float)
+0:5    Function Parameters: 
+0:?     Sequence
+0:5      Branch: Return with expression
+0:5        'v1' ( global 4-component vector of float)
+0:10  Function Definition: N2::getVec( ( temp 4-component vector of float)
+0:10    Function Parameters: 
+0:?     Sequence
+0:10      Branch: Return with expression
+0:10        'v2' ( global 4-component vector of float)
+0:12  Function Definition: N2::N3::getVec( ( temp 4-component vector of float)
+0:12    Function Parameters: 
+0:?     Sequence
+0:12      Branch: Return with expression
+0:12        'v2' ( global 4-component vector of float)
+0:15  Function Definition: N2::N3::C1::getVec( ( temp 4-component vector of float)
+0:15    Function Parameters: 
+0:15      '@this' ( temp structure{})
+0:?     Sequence
+0:15      Branch: Return with expression
+0:15        'v2' ( global 4-component vector of float)
+0:21  Function Definition: @main( ( temp 4-component vector of float)
+0:21    Function Parameters: 
+0:?     Sequence
+0:22      Branch: Return with expression
+0:22        add ( temp 4-component vector of float)
+0:22          add ( temp 4-component vector of float)
+0:22            add ( temp 4-component vector of float)
+0:22              Function Call: N1::getVec( ( temp 4-component vector of float)
+0:22              Function Call: N2::getVec( ( temp 4-component vector of float)
+0:22            Function Call: N2::N3::getVec( ( temp 4-component vector of float)
+0:22          vector-scale ( temp 4-component vector of float)
+0:22            Function Call: N2::N3::C1::getVec( ( temp 4-component vector of float)
+0:22            'N2::gf' ( global float)
+0:21  Function Definition: main( ( temp void)
+0:21    Function Parameters: 
+0:?     Sequence
+0:21      move second child to first child ( temp 4-component vector of float)
+0:?         '@entryPointOutput' (layout( location=0) out 4-component vector of float)
+0:21        Function Call: @main( ( temp 4-component vector of float)
+0:?   Linker Objects
+0:?     'v1' ( global 4-component vector of float)
+0:?     'v2' ( global 4-component vector of float)
+0:?     'N2::gf' ( global float)
+0:?     '@entryPointOutput' (layout( location=0) out 4-component vector of float)
+
+
+Linked fragment stage:
+
+
+Shader version: 450
+gl_FragCoord origin is upper left
+0:? Sequence
+0:5  Function Definition: N1::getVec( ( temp 4-component vector of float)
+0:5    Function Parameters: 
+0:?     Sequence
+0:5      Branch: Return with expression
+0:5        'v1' ( global 4-component vector of float)
+0:10  Function Definition: N2::getVec( ( temp 4-component vector of float)
+0:10    Function Parameters: 
+0:?     Sequence
+0:10      Branch: Return with expression
+0:10        'v2' ( global 4-component vector of float)
+0:12  Function Definition: N2::N3::getVec( ( temp 4-component vector of float)
+0:12    Function Parameters: 
+0:?     Sequence
+0:12      Branch: Return with expression
+0:12        'v2' ( global 4-component vector of float)
+0:15  Function Definition: N2::N3::C1::getVec( ( temp 4-component vector of float)
+0:15    Function Parameters: 
+0:15      '@this' ( temp structure{})
+0:?     Sequence
+0:15      Branch: Return with expression
+0:15        'v2' ( global 4-component vector of float)
+0:21  Function Definition: @main( ( temp 4-component vector of float)
+0:21    Function Parameters: 
+0:?     Sequence
+0:22      Branch: Return with expression
+0:22        add ( temp 4-component vector of float)
+0:22          add ( temp 4-component vector of float)
+0:22            add ( temp 4-component vector of float)
+0:22              Function Call: N1::getVec( ( temp 4-component vector of float)
+0:22              Function Call: N2::getVec( ( temp 4-component vector of float)
+0:22            Function Call: N2::N3::getVec( ( temp 4-component vector of float)
+0:22          vector-scale ( temp 4-component vector of float)
+0:22            Function Call: N2::N3::C1::getVec( ( temp 4-component vector of float)
+0:22            'N2::gf' ( global float)
+0:21  Function Definition: main( ( temp void)
+0:21    Function Parameters: 
+0:?     Sequence
+0:21      move second child to first child ( temp 4-component vector of float)
+0:?         '@entryPointOutput' (layout( location=0) out 4-component vector of float)
+0:21        Function Call: @main( ( temp 4-component vector of float)
+0:?   Linker Objects
+0:?     'v1' ( global 4-component vector of float)
+0:?     'v2' ( global 4-component vector of float)
+0:?     'N2::gf' ( global float)
+0:?     '@entryPointOutput' (layout( location=0) out 4-component vector of float)
+
+// Module Version 10000
+// Generated by (magic number): 80001
+// Id's are bound by 54
+
+                              Capability Shader
+               1:             ExtInstImport  "GLSL.std.450"
+                              MemoryModel Logical GLSL450
+                              EntryPoint Fragment 4  "main" 52
+                              ExecutionMode 4 OriginUpperLeft
+                              Name 4  "main"
+                              Name 9  "N1::getVec("
+                              Name 11  "N2::getVec("
+                              Name 13  "N2::N3::getVec("
+                              Name 15  "C1"
+                              Name 19  "N2::N3::C1::getVec("
+                              Name 18  "@this"
+                              Name 21  "@main("
+                              Name 24  "v1"
+                              Name 28  "v2"
+                              Name 45  "N2::gf"
+                              Name 52  "@entryPointOutput"
+                              Decorate 52(@entryPointOutput) Location 0
+               2:             TypeVoid
+               3:             TypeFunction 2
+               6:             TypeFloat 32
+               7:             TypeVector 6(float) 4
+               8:             TypeFunction 7(fvec4)
+          15(C1):             TypeStruct
+              16:             TypePointer Function 15(C1)
+              17:             TypeFunction 7(fvec4) 16(ptr)
+              23:             TypePointer Private 7(fvec4)
+          24(v1):     23(ptr) Variable Private
+          28(v2):     23(ptr) Variable Private
+              44:             TypePointer Private 6(float)
+      45(N2::gf):     44(ptr) Variable Private
+              51:             TypePointer Output 7(fvec4)
+52(@entryPointOutput):     51(ptr) Variable Output
+         4(main):           2 Function None 3
+               5:             Label
+              53:    7(fvec4) FunctionCall 21(@main()
+                              Store 52(@entryPointOutput) 53
+                              Return
+                              FunctionEnd
+  9(N1::getVec():    7(fvec4) Function None 8
+              10:             Label
+              25:    7(fvec4) Load 24(v1)
+                              ReturnValue 25
+                              FunctionEnd
+ 11(N2::getVec():    7(fvec4) Function None 8
+              12:             Label
+              29:    7(fvec4) Load 28(v2)
+                              ReturnValue 29
+                              FunctionEnd
+13(N2::N3::getVec():    7(fvec4) Function None 8
+              14:             Label
+              32:    7(fvec4) Load 28(v2)
+                              ReturnValue 32
+                              FunctionEnd
+19(N2::N3::C1::getVec():    7(fvec4) Function None 17
+       18(@this):     16(ptr) FunctionParameter
+              20:             Label
+              35:    7(fvec4) Load 28(v2)
+                              ReturnValue 35
+                              FunctionEnd
+      21(@main():    7(fvec4) Function None 8
+              22:             Label
+              38:    7(fvec4) FunctionCall 9(N1::getVec()
+              39:    7(fvec4) FunctionCall 11(N2::getVec()
+              40:    7(fvec4) FAdd 38 39
+              41:    7(fvec4) FunctionCall 13(N2::N3::getVec()
+              42:    7(fvec4) FAdd 40 41
+              43:    7(fvec4) FunctionCall 19(N2::N3::C1::getVec()
+              46:    6(float) Load 45(N2::gf)
+              47:    7(fvec4) VectorTimesScalar 43 46
+              48:    7(fvec4) FAdd 42 47
+                              ReturnValue 48
+                              FunctionEnd
diff --git a/Test/hlsl.namespace.frag b/Test/hlsl.namespace.frag
new file mode 100755 (executable)
index 0000000..76c3062
--- /dev/null
@@ -0,0 +1,23 @@
+static float4 v1;\r
+static float4 v2;\r
+\r
+namespace N1 {\r
+    float4 getVec() { return v1; }\r
+}\r
+\r
+namespace N2 {\r
+    static float gf;\r
+    float4 getVec() { return v2; }\r
+    namespace N3 {\r
+        float4 getVec() { return v2; }\r
+        \r
+        class C1 {\r
+            float4 getVec() { return v2; }\r
+        };\r
+    }\r
+}\r
+\r
+float4 main() : SV_Target0\r
+{\r
+    return N1::getVec() + N2::getVec() + N2::N3::getVec() + N2::N3::C1::getVec() * N2::gf;\r
+}\r
index f638909..5a18fcb 100644 (file)
@@ -2,5 +2,5 @@
 // For the version, it uses the latest git tag followed by the number of commits.
 // For the date, it uses the current date (when then script is run).
 
-#define GLSLANG_REVISION "Overload400-PrecQual.1939"
+#define GLSLANG_REVISION "Overload400-PrecQual.1946"
 #define GLSLANG_DATE "30-Mar-2017"
index 185435a..2b775e8 100644 (file)
@@ -161,6 +161,7 @@ INSTANTIATE_TEST_CASE_P(
         {"hlsl.logical.unary.frag", "main"},
         {"hlsl.logical.binary.frag", "main"},
         {"hlsl.logical.binary.vec.frag", "main"},
+        {"hlsl.namespace.frag", "main"},
         {"hlsl.matNx1.frag", "main"},
         {"hlsl.matrixSwizzle.vert", "ShaderFunction"},
         {"hlsl.mintypes.frag", "main"},
index 155a5b8..dd9cbf6 100755 (executable)
@@ -134,22 +134,17 @@ bool HlslGrammar::acceptIdentifier(HlslToken& idToken)
 }
 
 // compilationUnit
-//      : list of externalDeclaration
-//      |   SEMICOLONS
+//      : declaration_list EOF
 //
 bool HlslGrammar::acceptCompilationUnit()
 {
     TIntermNode* unitNode = nullptr;
 
-    while (! peekTokenClass(EHTokNone)) {
-        // HLSL allows semicolons between global declarations, e.g, between functions.
-        if (acceptTokenClass(EHTokSemicolon))
-            continue;
+    if (! acceptDeclarationList(unitNode))
+        return false;
 
-        // externalDeclaration
-        if (! acceptDeclaration(unitNode))
-            return false;
-    }
+    if (! peekTokenClass(EHTokNone))
+        return false;
 
     // set root of AST
     if (unitNode && !unitNode->getAsAggregate())
@@ -159,6 +154,34 @@ bool HlslGrammar::acceptCompilationUnit()
     return true;
 }
 
+// Recognize the following, but with the extra condition that it can be
+// successfully terminated by EOF or '}'.
+//
+// declaration_list
+//      : list of declaration_or_semicolon followed by EOF or RIGHT_BRACE
+//
+// declaration_or_semicolon
+//      : declaration
+//      : SEMICOLON
+//
+bool HlslGrammar::acceptDeclarationList(TIntermNode*& nodeList)
+{
+    do {
+        // HLSL allows extra semicolons between global declarations
+        do { } while (acceptTokenClass(EHTokSemicolon));
+
+        // EOF or RIGHT_BRACE
+        if (peekTokenClass(EHTokNone) || peekTokenClass(EHTokRightBrace))
+            return true;
+
+        // declaration
+        if (! acceptDeclaration(nodeList))
+            return false;
+    } while (true);
+
+    return true;
+}
+
 // sampler_state
 //      : LEFT_BRACE [sampler_state_assignment ... ] RIGHT_BRACE
 //
@@ -289,6 +312,7 @@ bool HlslGrammar::acceptSamplerDeclarationDX9(TType& /*type*/)
 //      | fully_specified_type identifier function_parameters post_decls compound_statement  // function definition
 //      | fully_specified_type identifier sampler_state post_decls compound_statement        // sampler definition
 //      | typedef declaration
+//      | NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE
 //
 // declarator_list
 //      : declarator COMMA declarator COMMA declarator...  // zero or more declarators
@@ -314,6 +338,30 @@ bool HlslGrammar::acceptSamplerDeclarationDX9(TType& /*type*/)
 //
 bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
 {
+    // NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE
+    if (acceptTokenClass(EHTokNamespace)) {
+        HlslToken namespaceToken;
+        if (!acceptIdentifier(namespaceToken)) {
+            expected("namespace name");
+            return false;
+        }
+        parseContext.pushNamespace(*namespaceToken.string);
+        if (!acceptTokenClass(EHTokLeftBrace)) {
+            expected("{");
+            return false;
+        }
+        if (!acceptDeclarationList(nodeList)) {
+            expected("declaration list");
+            return false;
+        }
+        if (!acceptTokenClass(EHTokRightBrace)) {
+            expected("}");
+            return false;
+        }
+        parseContext.popNamespace();
+        return true;
+    }
+
     bool declarator_list = false; // true when processing comma separation
 
     // attributes
@@ -342,15 +390,17 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
     HlslToken idToken;
     TIntermAggregate* initializers = nullptr;
     while (acceptIdentifier(idToken)) {
+        const TString *fullName = idToken.string;
+        if (parseContext.symbolTable.atGlobalLevel())
+            parseContext.getFullNamespaceName(fullName);
         if (peekTokenClass(EHTokLeftParen)) {
             // looks like function parameters
-            const TString* fnName = idToken.string;
 
             // Potentially rename shader entry point function.  No-op most of the time.
-            parseContext.renameShaderFunction(fnName);
+            parseContext.renameShaderFunction(fullName);
 
             // function_parameters
-            declarator.function = new TFunction(fnName, declaredType);
+            declarator.function = new TFunction(fullName, declaredType);
             if (!acceptFunctionParameters(*declarator.function)) {
                 expected("function parameter list");
                 return false;
@@ -424,19 +474,19 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
             // TODO: strings are not yet handled.
             if (variableType.getBasicType() != EbtString && parseContext.getAnnotationNestingLevel() == 0) {
                 if (typedefDecl)
-                    parseContext.declareTypedef(idToken.loc, *idToken.string, variableType);
+                    parseContext.declareTypedef(idToken.loc, *fullName, variableType);
                 else if (variableType.getBasicType() == EbtBlock)
-                    parseContext.declareBlock(idToken.loc, variableType, idToken.string);
+                    parseContext.declareBlock(idToken.loc, variableType, fullName);
                 else {
                     if (variableType.getQualifier().storage == EvqUniform && ! variableType.containsOpaque()) {
                         // this isn't really an individual variable, but a member of the $Global buffer
-                        parseContext.growGlobalUniformBlock(idToken.loc, variableType, *idToken.string);
+                        parseContext.growGlobalUniformBlock(idToken.loc, variableType, *fullName);
                     } else {
                         // Declare the variable and add any initializer code to the AST.
                         // The top-level node is always made into an aggregate, as that's
                         // historically how the AST has been.
                         initializers = intermediate.growAggregate(initializers,
-                            parseContext.declareVariable(idToken.loc, *idToken.string, variableType, expressionNode),
+                            parseContext.declareVariable(idToken.loc, *fullName, variableType, expressionNode),
                             idToken.loc);
                     }
                 }
@@ -2624,12 +2674,12 @@ bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node)
 //      : LEFT_PAREN expression RIGHT_PAREN
 //      | literal
 //      | constructor
-//      | identifier
+//      | IDENTIFIER [ COLONCOLON IDENTIFIER [ COLONCOLON IDENTIFIER ... ] ]
 //      | function_call
 //      | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET
 //      | postfix_expression DOT IDENTIFIER
 //      | postfix_expression DOT IDENTIFIER arguments
-//      | postfix_expression COLONCOLON IDENTIFIER arguments
+//      | postfix_expression arguments
 //      | postfix_expression INC_OP
 //      | postfix_expression DEC_OP
 //
@@ -2641,9 +2691,6 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node)
     // idToken will pick up either a variable or a function name in a function call
     HlslToken idToken;
 
-    // scopeBase will pick up the type symbol on the left of '::'
-    TSymbol* scope = nullptr;
-
     // Find something before the postfix operations, as they can't operate
     // on nothing.  So, no "return true", they fall through, only "return false".
     if (acceptTokenClass(EHTokLeftParen)) {
@@ -2657,21 +2704,26 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node)
             return false;
         }
     } else if (acceptLiteral(node)) {
-        // literal (nothing else to do yet), go on to the
+        // literal (nothing else to do yet)
     } else if (acceptConstructor(node)) {
         // constructor (nothing else to do yet)
     } else if (acceptIdentifier(idToken)) {
-        // user-type, identifier, or function name
-        if (peekTokenClass(EHTokColonColon)) {
-            TType type;
-            scope = parseContext.lookupUserType(*idToken.string, type);
-            if (scope == nullptr) {
-                expected("type left of ::");
+        // user-type, namespace name, variable, or function name
+        TString* fullName = idToken.string;
+        while (acceptTokenClass(EHTokColonColon)) {
+            // user-type or namespace name
+            fullName = NewPoolTString(fullName->c_str());
+            fullName->append(parseContext.scopeMangler);
+            if (acceptIdentifier(idToken))
+                fullName->append(*idToken.string);
+            else {
+                expected("identifier after ::");
                 return false;
             }
-        } else if (! peekTokenClass(EHTokLeftParen)) {
-            node = parseContext.handleVariable(idToken.loc, idToken.string);
-        } else if (acceptFunctionCall(idToken, node)) {
+        }
+        if (! peekTokenClass(EHTokLeftParen)) {
+            node = parseContext.handleVariable(idToken.loc, fullName);
+        } else if (acceptFunctionCall(idToken.loc, *fullName, node, nullptr)) {
             // function_call (nothing else to do yet)
         } else {
             expected("function call arguments");
@@ -2735,7 +2787,7 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node)
                 TIntermTyped* thisNode = node;
 
                 // arguments
-                if (! acceptFunctionCall(field, node, thisNode, scope)) {
+                if (! acceptFunctionCall(field.loc, *field.string, node, thisNode)) {
                     expected("function parameters");
                     return false;
                 }
@@ -2807,26 +2859,26 @@ bool HlslGrammar::acceptConstructor(TIntermTyped*& node)
 // function_call
 //      : [idToken] arguments
 //
-bool HlslGrammar::acceptFunctionCall(HlslToken callToken, TIntermTyped*& node, TIntermTyped* baseObject,
-                                     const TSymbol* scope)
+bool HlslGrammar::acceptFunctionCall(const TSourceLoc& loc, TString& name, TIntermTyped*& node, TIntermTyped* baseObject)
 {
     // name
     TString* functionName = nullptr;
-    if ((baseObject == nullptr && scope == nullptr)) {
-        functionName = callToken.string;
-    } else if (parseContext.isBuiltInMethod(callToken.loc, baseObject, *callToken.string)) {
+    if (baseObject == nullptr) {
+        functionName = &name;
+    } else if (parseContext.isBuiltInMethod(loc, baseObject, name)) {
         // Built-in methods are not in the symbol table as methods, but as global functions
         // taking an explicit 'this' as the first argument.
         functionName = NewPoolTString(BUILTIN_PREFIX);
-        functionName->append(*callToken.string);
+        functionName->append(name);
     } else {
+        if (! baseObject->getType().isStruct()) {
+            expected("structure");
+            return false;
+        }
         functionName = NewPoolTString("");
-        if (baseObject != nullptr)
-            functionName->append(baseObject->getType().getTypeName());
-        else if (scope != nullptr)
-            functionName->append(scope->getType().getTypeName());
+        functionName->append(baseObject->getType().getTypeName());
         parseContext.addScopeMangler(*functionName);
-        functionName->append(*callToken.string);
+        functionName->append(name);
     }
 
     // function
@@ -2842,7 +2894,7 @@ bool HlslGrammar::acceptFunctionCall(HlslToken callToken, TIntermTyped*& node, T
         return false;
 
     // call
-    node = parseContext.handleFunctionCall(callToken.loc, function, arguments);
+    node = parseContext.handleFunctionCall(loc, function, arguments);
 
     return true;
 }
index 38b2b58..e88d780 100755 (executable)
@@ -65,6 +65,7 @@ namespace glslang {
         void unimplemented(const char*);
         bool acceptIdentifier(HlslToken&);
         bool acceptCompilationUnit();
+        bool acceptDeclarationList(TIntermNode*&);
         bool acceptDeclaration(TIntermNode*&);
         bool acceptControlDeclaration(TIntermNode*& node);
         bool acceptSamplerDeclarationDX9(TType&);
@@ -103,8 +104,7 @@ namespace glslang {
         bool acceptUnaryExpression(TIntermTyped*&);
         bool acceptPostfixExpression(TIntermTyped*&);
         bool acceptConstructor(TIntermTyped*&);
-        bool acceptFunctionCall(HlslToken, TIntermTyped*&, TIntermTyped* objectBase = nullptr,
-                                const TSymbol* scope = nullptr);
+        bool acceptFunctionCall(const TSourceLoc&, TString& name, TIntermTyped*&, TIntermTyped* objectBase);
         bool acceptArguments(TFunction*, TIntermTyped*&);
         bool acceptLiteral(TIntermTyped*&);
         bool acceptCompoundStatement(TIntermNode*&);
index 7b0365f..6ddb39d 100755 (executable)
@@ -334,6 +334,7 @@ void HlslScanContext::fillInKeywordMap()
     (*KeywordMap)["tbuffer"] =                 EHTokTBuffer;
     (*KeywordMap)["typedef"] =                 EHTokTypedef;
     (*KeywordMap)["this"] =                    EHTokThis;
+    (*KeywordMap)["namespace"] =               EHTokNamespace;
 
     (*KeywordMap)["true"] =                    EHTokBoolConstant;
     (*KeywordMap)["false"] =                   EHTokBoolConstant;
@@ -828,6 +829,7 @@ EHlslTokenClass HlslScanContext::tokenizeIdentifier()
     case EHTokCBuffer:
     case EHTokTBuffer:
     case EHTokThis:
+    case EHTokNamespace:
         return keyword;
 
     case EHTokBoolConstant:
index 9f91906..cba0b96 100755 (executable)
@@ -274,6 +274,7 @@ enum EHlslTokenClass {
     EHTokTBuffer,
     EHTokTypedef,
     EHTokThis,
+    EHTokNamespace,
 
     // constant
     EHTokFloatConstant,