From 4960baaf663b18a6e0a58edb9073158ef7331930 Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Sun, 19 Mar 2017 18:09:59 -0600 Subject: [PATCH] HLSL: Basic turn on of non-static member functions. Still need: pass by reference in SPIR-V and symbol-table level for accessing 'this' members from member functions. --- SPIRV/GlslangToSpv.cpp | 3 +- .../hlsl.nonstaticMemberFunction.frag.out | 222 ++++++++++++++++++ Test/hlsl.nonstaticMemberFunction.frag | 22 ++ glslang/Include/revision.h | 2 +- gtests/Hlsl.FromFile.cpp | 1 + hlsl/hlslGrammar.cpp | 41 ++-- hlsl/hlslGrammar.h | 2 +- 7 files changed, 275 insertions(+), 18 deletions(-) create mode 100755 Test/baseResults/hlsl.nonstaticMemberFunction.frag.out create mode 100755 Test/hlsl.nonstaticMemberFunction.frag diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 9f272ee5..89f42177 100755 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -2747,7 +2747,8 @@ void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslF const glslang::TType& paramType = parameters[p]->getAsTyped()->getType(); spv::Id typeId = convertGlslangToSpvType(paramType); if (paramType.containsOpaque() || - (paramType.getBasicType() == glslang::EbtBlock && paramType.getQualifier().storage == glslang::EvqBuffer)) + (paramType.getBasicType() == glslang::EbtBlock && + paramType.getQualifier().storage == glslang::EvqBuffer)) typeId = builder.makePointer(TranslateStorageClass(paramType), typeId); else if (paramType.getQualifier().storage != glslang::EvqConstReadOnly) typeId = builder.makePointer(spv::StorageClassFunction, typeId); diff --git a/Test/baseResults/hlsl.nonstaticMemberFunction.frag.out b/Test/baseResults/hlsl.nonstaticMemberFunction.frag.out new file mode 100755 index 00000000..142ce08d --- /dev/null +++ b/Test/baseResults/hlsl.nonstaticMemberFunction.frag.out @@ -0,0 +1,222 @@ +hlsl.nonstaticMemberFunction.frag +Shader version: 450 +gl_FragCoord origin is upper left +0:? Sequence +0:5 Function Definition: Test::memFun(vf4; ( temp 4-component vector of float) +0:5 Function Parameters: +0:5 'this' ( temp structure{ temp 4-component vector of float memVar, temp int i}) +0:5 'a' ( in 4-component vector of float) +0:? Sequence +0:6 Branch: Return with expression +0:6 vector-scale ( temp 4-component vector of float) +0:6 Constant: +0:6 2.000000 +0:6 'a' ( in 4-component vector of float) +0:9 Function Definition: Test::memFun(i1; ( temp int) +0:9 Function Parameters: +0:9 'this' ( temp structure{ temp 4-component vector of float memVar, temp int i}) +0:9 'a' ( in int) +0:? Sequence +0:10 Branch: Return with expression +0:10 add ( temp int) +0:10 Constant: +0:10 2 (const int) +0:10 'a' ( in int) +0:16 Function Definition: @main( ( temp 4-component vector of float) +0:16 Function Parameters: +0:? Sequence +0:18 Sequence +0:18 move second child to first child ( temp 4-component vector of float) +0:18 'f4' ( temp 4-component vector of float) +0:? Constant: +0:? 1.000000 +0:? 1.000000 +0:? 1.000000 +0:? 1.000000 +0:19 add second child into first child ( temp 4-component vector of float) +0:19 'f4' ( temp 4-component vector of float) +0:19 Function Call: Test::memFun(vf4; ( temp 4-component vector of float) +0:19 'test' ( temp structure{ temp 4-component vector of float memVar, temp int i}) +0:? Constant: +0:? 5.000000 +0:? 5.000000 +0:? 5.000000 +0:? 5.000000 +0:20 add second child into first child ( temp 4-component vector of float) +0:20 'f4' ( temp 4-component vector of float) +0:20 Convert int to float ( temp float) +0:20 Function Call: Test::memFun(i1; ( temp int) +0:20 'test' ( temp structure{ temp 4-component vector of float memVar, temp int i}) +0:20 Constant: +0:20 7 (const int) +0:21 Branch: Return with expression +0:21 'f4' ( temp 4-component vector of float) +0:16 Function Definition: main( ( temp void) +0:16 Function Parameters: +0:? Sequence +0:16 move second child to first child ( temp 4-component vector of float) +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) +0:16 Function Call: @main( ( temp 4-component vector of float) +0:? Linker Objects +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: Test::memFun(vf4; ( temp 4-component vector of float) +0:5 Function Parameters: +0:5 'this' ( temp structure{ temp 4-component vector of float memVar, temp int i}) +0:5 'a' ( in 4-component vector of float) +0:? Sequence +0:6 Branch: Return with expression +0:6 vector-scale ( temp 4-component vector of float) +0:6 Constant: +0:6 2.000000 +0:6 'a' ( in 4-component vector of float) +0:9 Function Definition: Test::memFun(i1; ( temp int) +0:9 Function Parameters: +0:9 'this' ( temp structure{ temp 4-component vector of float memVar, temp int i}) +0:9 'a' ( in int) +0:? Sequence +0:10 Branch: Return with expression +0:10 add ( temp int) +0:10 Constant: +0:10 2 (const int) +0:10 'a' ( in int) +0:16 Function Definition: @main( ( temp 4-component vector of float) +0:16 Function Parameters: +0:? Sequence +0:18 Sequence +0:18 move second child to first child ( temp 4-component vector of float) +0:18 'f4' ( temp 4-component vector of float) +0:? Constant: +0:? 1.000000 +0:? 1.000000 +0:? 1.000000 +0:? 1.000000 +0:19 add second child into first child ( temp 4-component vector of float) +0:19 'f4' ( temp 4-component vector of float) +0:19 Function Call: Test::memFun(vf4; ( temp 4-component vector of float) +0:19 'test' ( temp structure{ temp 4-component vector of float memVar, temp int i}) +0:? Constant: +0:? 5.000000 +0:? 5.000000 +0:? 5.000000 +0:? 5.000000 +0:20 add second child into first child ( temp 4-component vector of float) +0:20 'f4' ( temp 4-component vector of float) +0:20 Convert int to float ( temp float) +0:20 Function Call: Test::memFun(i1; ( temp int) +0:20 'test' ( temp structure{ temp 4-component vector of float memVar, temp int i}) +0:20 Constant: +0:20 7 (const int) +0:21 Branch: Return with expression +0:21 'f4' ( temp 4-component vector of float) +0:16 Function Definition: main( ( temp void) +0:16 Function Parameters: +0:? Sequence +0:16 move second child to first child ( temp 4-component vector of float) +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) +0:16 Function Call: @main( ( temp 4-component vector of float) +0:? Linker Objects +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 61 + + Capability Shader + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 4 "main" 59 + ExecutionMode 4 OriginUpperLeft + Name 4 "main" + Name 9 "Test" + MemberName 9(Test) 0 "memVar" + MemberName 9(Test) 1 "i" + Name 15 "Test::memFun(vf4;" + Name 13 "this" + Name 14 "a" + Name 21 "Test::memFun(i1;" + Name 19 "this" + Name 20 "a" + Name 24 "@main(" + Name 36 "f4" + Name 39 "test" + Name 42 "param" + Name 43 "param" + Name 48 "param" + Name 49 "param" + Name 59 "@entryPointOutput" + Decorate 59(@entryPointOutput) Location 0 + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypeVector 6(float) 4 + 8: TypeInt 32 1 + 9(Test): TypeStruct 7(fvec4) 8(int) + 10: TypePointer Function 9(Test) + 11: TypePointer Function 7(fvec4) + 12: TypeFunction 7(fvec4) 10(ptr) 11(ptr) + 17: TypePointer Function 8(int) + 18: TypeFunction 8(int) 10(ptr) 17(ptr) + 23: TypeFunction 7(fvec4) + 26: 6(float) Constant 1073741824 + 31: 8(int) Constant 2 + 37: 6(float) Constant 1065353216 + 38: 7(fvec4) ConstantComposite 37 37 37 37 + 40: 6(float) Constant 1084227584 + 41: 7(fvec4) ConstantComposite 40 40 40 40 + 47: 8(int) Constant 7 + 58: TypePointer Output 7(fvec4) +59(@entryPointOutput): 58(ptr) Variable Output + 4(main): 2 Function None 3 + 5: Label + 60: 7(fvec4) FunctionCall 24(@main() + Store 59(@entryPointOutput) 60 + Return + FunctionEnd +15(Test::memFun(vf4;): 7(fvec4) Function None 12 + 13(this): 10(ptr) FunctionParameter + 14(a): 11(ptr) FunctionParameter + 16: Label + 27: 7(fvec4) Load 14(a) + 28: 7(fvec4) VectorTimesScalar 27 26 + ReturnValue 28 + FunctionEnd +21(Test::memFun(i1;): 8(int) Function None 18 + 19(this): 10(ptr) FunctionParameter + 20(a): 17(ptr) FunctionParameter + 22: Label + 32: 8(int) Load 20(a) + 33: 8(int) IAdd 31 32 + ReturnValue 33 + FunctionEnd + 24(@main(): 7(fvec4) Function None 23 + 25: Label + 36(f4): 11(ptr) Variable Function + 39(test): 10(ptr) Variable Function + 42(param): 10(ptr) Variable Function + 43(param): 11(ptr) Variable Function + 48(param): 10(ptr) Variable Function + 49(param): 17(ptr) Variable Function + Store 36(f4) 38 + Store 43(param) 41 + 44: 7(fvec4) FunctionCall 15(Test::memFun(vf4;) 42(param) 43(param) + 45: 7(fvec4) Load 36(f4) + 46: 7(fvec4) FAdd 45 44 + Store 36(f4) 46 + Store 49(param) 47 + 50: 8(int) FunctionCall 21(Test::memFun(i1;) 48(param) 49(param) + 51: 6(float) ConvertSToF 50 + 52: 7(fvec4) Load 36(f4) + 53: 7(fvec4) CompositeConstruct 51 51 51 51 + 54: 7(fvec4) FAdd 52 53 + Store 36(f4) 54 + 55: 7(fvec4) Load 36(f4) + ReturnValue 55 + FunctionEnd diff --git a/Test/hlsl.nonstaticMemberFunction.frag b/Test/hlsl.nonstaticMemberFunction.frag new file mode 100755 index 00000000..c4f2b7b0 --- /dev/null +++ b/Test/hlsl.nonstaticMemberFunction.frag @@ -0,0 +1,22 @@ +struct Test +{ + float4 memVar; + float4 memFun(float4 a) : SV_Position + { + return 2 * a; + } + int memFun(int a) : SV_Position + { + return 2 + a; + } + int i; +}; + +float4 main() : SV_Target0 +{ + Test test; + float4 f4 = float4(1.0,1.0,1.0,1.0); + f4 += test.memFun(float4(5.0f,5.0f,5.0f,5.0f)); + f4 += test.memFun(7); + return f4; +} diff --git a/glslang/Include/revision.h b/glslang/Include/revision.h index eaae2ce6..48929976 100644 --- a/glslang/Include/revision.h +++ b/glslang/Include/revision.h @@ -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.1921" +#define GLSLANG_REVISION "Overload400-PrecQual.1922" #define GLSLANG_DATE "19-Mar-2017" diff --git a/gtests/Hlsl.FromFile.cpp b/gtests/Hlsl.FromFile.cpp index f2b480cb..06dc82f0 100644 --- a/gtests/Hlsl.FromFile.cpp +++ b/gtests/Hlsl.FromFile.cpp @@ -164,6 +164,7 @@ INSTANTIATE_TEST_CASE_P( {"hlsl.multiEntry.vert", "RealEntrypoint"}, {"hlsl.multiReturn.frag", "main"}, {"hlsl.matrixindex.frag", "main"}, + {"hlsl.nonstaticMemberFunction.frag", "main"}, {"hlsl.numericsuffixes.frag", "main"}, {"hlsl.numthreads.comp", "main_aux1"}, {"hlsl.overload.frag", "PixelShaderFunction"}, diff --git a/hlsl/hlslGrammar.cpp b/hlsl/hlslGrammar.cpp index 67216684..3891734a 100755 --- a/hlsl/hlslGrammar.cpp +++ b/hlsl/hlslGrammar.cpp @@ -1831,6 +1831,15 @@ bool HlslGrammar::acceptStruct(TType& type, TIntermNode*& nodeList) parseContext.declareStruct(token.loc, structName, type); + // For member functions: now that we know the type of 'this', go back and + // - add their implicit argument with 'this' (not to the mangling, just the argument list) + // - parse the functions, their tokens were saved for deferred parsing (now) + for (int b = 0; b < (int)functionDeclarators.size(); ++b) { + // update signature + if (functionDeclarators[b].function->hasImplicitThis()) + functionDeclarators[b].function->addThisParameter(type); + } + // All member functions get parsed inside the class/struct namespace and with the // class/struct members in a symbol-table level. parseContext.pushNamespace(structName); @@ -2059,6 +2068,8 @@ bool HlslGrammar::acceptMemberFunctionDefinition(TIntermNode*& nodeList, const T TString* functionName = parseContext.getFullNamespaceName(memberName); declarator.function = new TFunction(functionName, type); + if (type.getQualifier().storage == EvqTemporary) + declarator.function->setImplicitThis(); // function_parameters if (acceptFunctionParameters(*declarator.function)) { @@ -2067,11 +2078,6 @@ bool HlslGrammar::acceptMemberFunctionDefinition(TIntermNode*& nodeList, const T // compound_statement (function body definition) if (peekTokenClass(EHTokLeftBrace)) { - if (declarator.function->getType().getQualifier().storage != EvqGlobal) { - expected("only static member functions are accepted"); - return false; - } - declarator.loc = token.loc; declarator.body = new TVector; accepted = acceptFunctionDefinition(declarator, nodeList, declarator.body); @@ -2221,7 +2227,7 @@ bool HlslGrammar::acceptFunctionDefinition(TFunctionDeclarator& declarator, TInt if (deferredTokens) return captureBlockTokens(*deferredTokens); else - return acceptFunctionBody(declarator, nodeList); + return acceptFunctionBody(declarator, nodeList); } bool HlslGrammar::acceptFunctionBody(TFunctionDeclarator& declarator, TIntermNode*& nodeList) @@ -2609,7 +2615,7 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) HlslToken idToken; // scopeBase will pick up the type symbol on the left of '::' - TSymbol* scopeBase = nullptr; + 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". @@ -2631,8 +2637,8 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) // user-type, identifier, or function name if (peekTokenClass(EHTokColonColon)) { TType type; - scopeBase = parseContext.lookupUserType(*idToken.string, type); - if (scopeBase == nullptr) { + scope = parseContext.lookupUserType(*idToken.string, type); + if (scope == nullptr) { expected("type left of ::"); return false; } @@ -2702,7 +2708,7 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) TIntermTyped* thisNode = node; // arguments - if (! acceptFunctionCall(field, node, thisNode, scopeBase)) { + if (! acceptFunctionCall(field, node, thisNode, scope)) { expected("function parameters"); return false; } @@ -2775,16 +2781,21 @@ bool HlslGrammar::acceptConstructor(TIntermTyped*& node) // : [idToken] arguments // bool HlslGrammar::acceptFunctionCall(HlslToken callToken, TIntermTyped*& node, TIntermTyped* baseObject, - const TSymbol* baseType) + const TSymbol* scope) { // name TString* functionName = nullptr; - if ((baseObject == nullptr && baseType == nullptr) - || parseContext.isBuiltInMethod(callToken.loc, baseObject, *callToken.string)) + if ((baseObject == nullptr && scope == nullptr) || + parseContext.isBuiltInMethod(callToken.loc, baseObject, *callToken.string)) { + // Built-in methods are not in the symbol table as methods, but as global functions + // taking an explicit 'this' as the first argument. functionName = callToken.string; - else { + } else { functionName = NewPoolTString(""); - functionName->append(baseType->getType().getTypeName()); + if (baseObject != nullptr) + functionName->append(baseObject->getType().getTypeName()); + else if (scope != nullptr) + functionName->append(scope->getType().getTypeName()); parseContext.addScopeMangler(*functionName); functionName->append(*callToken.string); } diff --git a/hlsl/hlslGrammar.h b/hlsl/hlslGrammar.h index 968364fa..1a3abf10 100755 --- a/hlsl/hlslGrammar.h +++ b/hlsl/hlslGrammar.h @@ -105,7 +105,7 @@ namespace glslang { bool acceptPostfixExpression(TIntermTyped*&); bool acceptConstructor(TIntermTyped*&); bool acceptFunctionCall(HlslToken, TIntermTyped*&, TIntermTyped* objectBase = nullptr, - const TSymbol* typeBase = nullptr); + const TSymbol* scope = nullptr); bool acceptArguments(TFunction*, TIntermTyped*&); bool acceptLiteral(TIntermTyped*&); bool acceptCompoundStatement(TIntermNode*&); -- 2.34.1