From 0a2a0cd3a44ea4675c42461dc9ea75e918c58781 Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Tue, 16 May 2017 23:16:26 -0600 Subject: [PATCH] HLSL: Implement member functions calling member functions. --- Test/baseResults/hlsl.memberFunCall.frag.out | 264 +++++++++++++++++++ Test/hlsl.memberFunCall.frag | 16 ++ glslang/MachineIndependent/SymbolTable.h | 18 +- gtests/Hlsl.FromFile.cpp | 1 + hlsl/hlslGrammar.cpp | 2 +- hlsl/hlslParseHelper.cpp | 63 ++++- hlsl/hlslParseHelper.h | 6 +- 7 files changed, 351 insertions(+), 19 deletions(-) create mode 100755 Test/baseResults/hlsl.memberFunCall.frag.out create mode 100644 Test/hlsl.memberFunCall.frag diff --git a/Test/baseResults/hlsl.memberFunCall.frag.out b/Test/baseResults/hlsl.memberFunCall.frag.out new file mode 100755 index 000000000..97743606c --- /dev/null +++ b/Test/baseResults/hlsl.memberFunCall.frag.out @@ -0,0 +1,264 @@ +hlsl.memberFunCall.frag +Shader version: 500 +gl_FragCoord origin is upper left +0:? Sequence +0:1 Function Definition: method3(f1; ( temp float) +0:1 Function Parameters: +0:1 'a' ( in float) +0:? Sequence +0:1 Branch: Return with expression +0:1 Constant: +0:1 1.000000 +0:4 Function Definition: myContext::method1( ( temp float) +0:4 Function Parameters: +0:4 '@this' ( temp structure{ temp float f}) +0:? Sequence +0:4 Branch: Return with expression +0:4 Function Call: myContext::method2( ( temp float) +0:? '@this' ( temp structure{ temp float f}) +0:5 Function Definition: myContext::method2( ( temp float) +0:5 Function Parameters: +0:5 '@this' ( temp structure{ temp float f}) +0:? Sequence +0:5 Branch: Return with expression +0:5 Function Call: myContext::method3(f1; ( temp float) +0:? '@this' ( temp structure{ temp float f}) +0:5 Constant: +0:5 1.000000 +0:6 Function Definition: myContext::method3(f1; ( temp float) +0:6 Function Parameters: +0:6 '@this' ( temp structure{ temp float f}) +0:6 'a' ( in float) +0:? Sequence +0:6 Branch: Return with expression +0:6 Function Call: myContext::method4(f1;f1; ( temp float) +0:? '@this' ( temp structure{ temp float f}) +0:6 'a' ( in float) +0:6 'a' ( in float) +0:7 Function Definition: myContext::method4(f1;f1; ( temp float) +0:7 Function Parameters: +0:7 '@this' ( temp structure{ temp float f}) +0:7 'a' ( in float) +0:7 'b' ( in float) +0:? Sequence +0:7 Branch: Return with expression +0:7 add ( temp float) +0:7 add ( temp float) +0:7 'a' ( in float) +0:7 'b' ( in float) +0:7 f: direct index for structure ( temp float) +0:7 '@this' ( temp structure{ temp float f}) +0:7 Constant: +0:7 0 (const uint) +0:12 Function Definition: @main( ( temp 4-component vector of float) +0:12 Function Parameters: +0:? Sequence +0:14 move second child to first child ( temp float) +0:14 f: direct index for structure ( temp float) +0:14 'context' ( temp structure{ temp float f}) +0:14 Constant: +0:14 0 (const int) +0:14 Constant: +0:14 3.000000 +0:15 Branch: Return with expression +0:15 Construct vec4 ( temp 4-component vector of float) +0:15 Function Call: myContext::method1( ( temp float) +0:15 'context' ( temp structure{ temp float f}) +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:? '@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:1 Function Definition: method3(f1; ( temp float) +0:1 Function Parameters: +0:1 'a' ( in float) +0:? Sequence +0:1 Branch: Return with expression +0:1 Constant: +0:1 1.000000 +0:4 Function Definition: myContext::method1( ( temp float) +0:4 Function Parameters: +0:4 '@this' ( temp structure{ temp float f}) +0:? Sequence +0:4 Branch: Return with expression +0:4 Function Call: myContext::method2( ( temp float) +0:? '@this' ( temp structure{ temp float f}) +0:5 Function Definition: myContext::method2( ( temp float) +0:5 Function Parameters: +0:5 '@this' ( temp structure{ temp float f}) +0:? Sequence +0:5 Branch: Return with expression +0:5 Function Call: myContext::method3(f1; ( temp float) +0:? '@this' ( temp structure{ temp float f}) +0:5 Constant: +0:5 1.000000 +0:6 Function Definition: myContext::method3(f1; ( temp float) +0:6 Function Parameters: +0:6 '@this' ( temp structure{ temp float f}) +0:6 'a' ( in float) +0:? Sequence +0:6 Branch: Return with expression +0:6 Function Call: myContext::method4(f1;f1; ( temp float) +0:? '@this' ( temp structure{ temp float f}) +0:6 'a' ( in float) +0:6 'a' ( in float) +0:7 Function Definition: myContext::method4(f1;f1; ( temp float) +0:7 Function Parameters: +0:7 '@this' ( temp structure{ temp float f}) +0:7 'a' ( in float) +0:7 'b' ( in float) +0:? Sequence +0:7 Branch: Return with expression +0:7 add ( temp float) +0:7 add ( temp float) +0:7 'a' ( in float) +0:7 'b' ( in float) +0:7 f: direct index for structure ( temp float) +0:7 '@this' ( temp structure{ temp float f}) +0:7 Constant: +0:7 0 (const uint) +0:12 Function Definition: @main( ( temp 4-component vector of float) +0:12 Function Parameters: +0:? Sequence +0:14 move second child to first child ( temp float) +0:14 f: direct index for structure ( temp float) +0:14 'context' ( temp structure{ temp float f}) +0:14 Constant: +0:14 0 (const int) +0:14 Constant: +0:14 3.000000 +0:15 Branch: Return with expression +0:15 Construct vec4 ( temp 4-component vector of float) +0:15 Function Call: myContext::method1( ( temp float) +0:15 'context' ( temp structure{ temp float f}) +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:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 73 + + Capability Shader + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 4 "main" 71 + ExecutionMode 4 OriginUpperLeft + Source HLSL 500 + Name 4 "main" + Name 10 "method3(f1;" + Name 9 "a" + Name 12 "myContext" + MemberName 12(myContext) 0 "f" + Name 16 "myContext::method1(" + Name 15 "@this" + Name 19 "myContext::method2(" + Name 18 "@this" + Name 24 "myContext::method3(f1;" + Name 22 "@this" + Name 23 "a" + Name 30 "myContext::method4(f1;f1;" + Name 27 "@this" + Name 28 "a" + Name 29 "b" + Name 34 "@main(" + Name 42 "param" + Name 46 "param" + Name 48 "param" + Name 63 "context" + Name 71 "@entryPointOutput" + Decorate 71(@entryPointOutput) Location 0 + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypePointer Function 6(float) + 8: TypeFunction 6(float) 7(ptr) + 12(myContext): TypeStruct 6(float) + 13: TypePointer Function 12(myContext) + 14: TypeFunction 6(float) 13(ptr) + 21: TypeFunction 6(float) 13(ptr) 7(ptr) + 26: TypeFunction 6(float) 13(ptr) 7(ptr) 7(ptr) + 32: TypeVector 6(float) 4 + 33: TypeFunction 32(fvec4) + 36: 6(float) Constant 1065353216 + 56: TypeInt 32 1 + 57: 56(int) Constant 0 + 64: 6(float) Constant 1077936128 + 70: TypePointer Output 32(fvec4) +71(@entryPointOutput): 70(ptr) Variable Output + 4(main): 2 Function None 3 + 5: Label + 72: 32(fvec4) FunctionCall 34(@main() + Store 71(@entryPointOutput) 72 + Return + FunctionEnd + 10(method3(f1;): 6(float) Function None 8 + 9(a): 7(ptr) FunctionParameter + 11: Label + ReturnValue 36 + FunctionEnd +16(myContext::method1(): 6(float) Function None 14 + 15(@this): 13(ptr) FunctionParameter + 17: Label + 39: 6(float) FunctionCall 19(myContext::method2() 15(@this) + ReturnValue 39 + FunctionEnd +19(myContext::method2(): 6(float) Function None 14 + 18(@this): 13(ptr) FunctionParameter + 20: Label + 42(param): 7(ptr) Variable Function + Store 42(param) 36 + 43: 6(float) FunctionCall 24(myContext::method3(f1;) 18(@this) 42(param) + ReturnValue 43 + FunctionEnd +24(myContext::method3(f1;): 6(float) Function None 21 + 22(@this): 13(ptr) FunctionParameter + 23(a): 7(ptr) FunctionParameter + 25: Label + 46(param): 7(ptr) Variable Function + 48(param): 7(ptr) Variable Function + 47: 6(float) Load 23(a) + Store 46(param) 47 + 49: 6(float) Load 23(a) + Store 48(param) 49 + 50: 6(float) FunctionCall 30(myContext::method4(f1;f1;) 22(@this) 46(param) 48(param) + ReturnValue 50 + FunctionEnd +30(myContext::method4(f1;f1;): 6(float) Function None 26 + 27(@this): 13(ptr) FunctionParameter + 28(a): 7(ptr) FunctionParameter + 29(b): 7(ptr) FunctionParameter + 31: Label + 53: 6(float) Load 28(a) + 54: 6(float) Load 29(b) + 55: 6(float) FAdd 53 54 + 58: 7(ptr) AccessChain 27(@this) 57 + 59: 6(float) Load 58 + 60: 6(float) FAdd 55 59 + ReturnValue 60 + FunctionEnd + 34(@main(): 32(fvec4) Function None 33 + 35: Label + 63(context): 13(ptr) Variable Function + 65: 7(ptr) AccessChain 63(context) 57 + Store 65 64 + 66: 6(float) FunctionCall 16(myContext::method1() 63(context) + 67: 32(fvec4) CompositeConstruct 66 66 66 66 + ReturnValue 67 + FunctionEnd diff --git a/Test/hlsl.memberFunCall.frag b/Test/hlsl.memberFunCall.frag new file mode 100644 index 000000000..27d3f6e01 --- /dev/null +++ b/Test/hlsl.memberFunCall.frag @@ -0,0 +1,16 @@ +float method3(float a) { return 1.0; } + +struct myContext { + float method1() { return method2(); } + float method2() { return method3(1.0); } + float method3(float a) { return method4(a, a); } + float method4(float a, float b) { return a + b + f; } + float f; +}; + +float4 main() : SV_TARGET0 +{ + myContext context; + context.f = 3.0; + return (float4)context.method1(); +} diff --git a/glslang/MachineIndependent/SymbolTable.h b/glslang/MachineIndependent/SymbolTable.h index 3d88bc277..f928b7aed 100644 --- a/glslang/MachineIndependent/SymbolTable.h +++ b/glslang/MachineIndependent/SymbolTable.h @@ -262,6 +262,12 @@ public: mangledName.insert(0, prefix); } + virtual void removePrefix(const TString& prefix) + { + assert(mangledName.compare(0, prefix.size(), prefix) == 0); + mangledName.erase(0, prefix.size()); + } + virtual const TString& getMangledName() const override { return mangledName; } virtual const TType& getType() const override { return returnType; } virtual TBuiltInVariable getDeclaredBuiltInType() const { return declaredBuiltIn; } @@ -686,19 +692,27 @@ public: // Normal find of a symbol, that can optionally say whether the symbol was found // at a built-in level or the current top-scope level. - TSymbol* find(const TString& name, bool* builtIn = 0, bool *currentScope = 0) + TSymbol* find(const TString& name, bool* builtIn = 0, bool* currentScope = 0, int* thisDepthP = 0) { int level = currentLevel(); TSymbol* symbol; + int thisDepth = 0; do { + if (table[level]->isThisLevel()) + ++thisDepth; symbol = table[level]->find(name); --level; - } while (symbol == 0 && level >= 0); + } while (symbol == nullptr && level >= 0); level++; if (builtIn) *builtIn = isBuiltInLevel(level); if (currentScope) *currentScope = isGlobalLevel(currentLevel()) || level == currentLevel(); // consider shared levels as "current scope" WRT user globals + if (thisDepthP != nullptr) { + if (! table[level]->isThisLevel()) + thisDepth = 0; + *thisDepthP = thisDepth; + } return symbol; } diff --git a/gtests/Hlsl.FromFile.cpp b/gtests/Hlsl.FromFile.cpp index 9612a4cfb..d1800d787 100644 --- a/gtests/Hlsl.FromFile.cpp +++ b/gtests/Hlsl.FromFile.cpp @@ -184,6 +184,7 @@ INSTANTIATE_TEST_CASE_P( {"hlsl.nonint-index.frag", "main"}, {"hlsl.matNx1.frag", "main"}, {"hlsl.matrixSwizzle.vert", "ShaderFunction"}, + {"hlsl.memberFunCall.frag", "main"}, {"hlsl.mintypes.frag", "main"}, {"hlsl.multiEntry.vert", "RealEntrypoint"}, {"hlsl.multiReturn.frag", "main"}, diff --git a/hlsl/hlslGrammar.cpp b/hlsl/hlslGrammar.cpp index cdf8a0777..69155850a 100755 --- a/hlsl/hlslGrammar.cpp +++ b/hlsl/hlslGrammar.cpp @@ -1933,7 +1933,7 @@ bool HlslGrammar::acceptStruct(TType& type, TIntermNode*& nodeList) // All member functions get parsed inside the class/struct namespace and with the // class/struct members in a symbol-table level. parseContext.pushNamespace(structName); - parseContext.pushThisScope(type); + parseContext.pushThisScope(type, functionDeclarators); bool deferredSuccess = true; for (int b = 0; b < (int)functionDeclarators.size() && deferredSuccess; ++b) { // parse body diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp index 85d973dd2..70a2a6559 100755 --- a/hlsl/hlslParseHelper.cpp +++ b/hlsl/hlslParseHelper.cpp @@ -4319,6 +4319,7 @@ TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunct // const TFunction* fnCandidate = nullptr; bool builtIn = false; + int thisDepth = 0; // TODO: this needs improvement: there's no way at present to look up a signature in // the symbol table for an arbitrary type. This is a temporary hack until that ability exists. @@ -4351,7 +4352,7 @@ TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunct } if (fnCandidate == nullptr) - fnCandidate = findFunction(loc, *function, builtIn, arguments); + fnCandidate = findFunction(loc, *function, builtIn, thisDepth, arguments); if (fnCandidate) { // This is a declared function that might map to @@ -4363,6 +4364,18 @@ TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunct if (builtIn && fnCandidate->getNumExtensions()) requireExtensions(loc, fnCandidate->getNumExtensions(), fnCandidate->getExtensions(), fnCandidate->getName().c_str()); + // turn an implicit member-function resolution into an explicit call + TString callerName; + if (thisDepth == 0) + callerName = fnCandidate->getMangledName(); + else { + // get the explicit (full) name of the function + callerName = currentTypePrefix[currentTypePrefix.size() - thisDepth]; + callerName += fnCandidate->getMangledName(); + // insert the implicit calling argument + pushFrontArguments(intermediate.addSymbol(*getImplicitThis(thisDepth)), arguments); + } + // Convert 'in' arguments if (arguments) addInputArgumentConversions(*fnCandidate, arguments); @@ -4383,14 +4396,14 @@ TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunct // It could still be a built-in function, but only if PureOperatorBuiltins == false. result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc); TIntermAggregate* call = result->getAsAggregate(); - call->setName(fnCandidate->getMangledName()); + call->setName(callerName); // this is how we know whether the given function is a built-in function or a user-defined function // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also // if builtIn == true, it's definitely a built-in function with EOpNull if (! builtIn) { call->setUserDefined(); - intermediate.addToCallGraph(infoSink, currentCaller, fnCandidate->getMangledName()); + intermediate.addToCallGraph(infoSink, currentCaller, callerName); } } @@ -4427,6 +4440,19 @@ TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunct return result; } +// An initial argument list is difficult: it can be null, or a single node, +// or an aggregate if more than one argument. Add one to the front, maintaining +// this lack of uniformity. +void HlslParseContext::pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments) +{ + if (arguments == nullptr) + arguments = front; + else if (arguments->getAsAggregate() != nullptr) + arguments->getAsAggregate()->getSequence().insert(arguments->getAsAggregate()->getSequence().begin(), front); + else + arguments = intermediate.growAggregate(front, arguments); +} + // // Add any needed implicit conversions for function-call arguments to input parameters. // @@ -6184,18 +6210,17 @@ void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQuali // // Return the function symbol if found, otherwise nullptr. // -const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, +const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth, TIntermTyped*& args) { - // const TFunction* function = nullptr; - if (symbolTable.isFunctionNameVariable(call.getName())) { error(loc, "can't use function syntax on variable", call.getName().c_str(), ""); return nullptr; } // first, look for an exact match - TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + bool dummyScope; + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn, &dummyScope, &thisDepth); if (symbol) return symbol->getAsFunction(); @@ -6205,7 +6230,7 @@ const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction TVector candidateList; symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); - // These builtin ops can accept any type, so we bypass the argument selection + // These built-in ops can accept any type, so we bypass the argument selection if (candidateList.size() == 1 && builtIn && (candidateList[0]->getBuiltInOp() == EOpMethodAppend || candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip || @@ -7782,11 +7807,23 @@ TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* ex // Make a new symbol-table level that is made out of the members of a structure. // This should be done as an anonymous struct (name is "") so that the symbol table -// finds the members with on explicit reference to a 'this' variable. -void HlslParseContext::pushThisScope(const TType& thisStruct) +// finds the members with no explicit reference to a 'this' variable. +void HlslParseContext::pushThisScope(const TType& thisStruct, const TVector& functionDeclarators) { + // member variables TVariable& thisVariable = *new TVariable(NewPoolTString(""), thisStruct); symbolTable.pushThis(thisVariable); + + // member functions + for (auto it = functionDeclarators.begin(); it != functionDeclarators.end(); ++it) { + // member should have a prefix matching currentTypePrefix.back() + // but, symbol lookup within the class scope will just use the + // unprefixed name. Hence, there are two: one fully prefixed and + // one with no prefix. + TFunction& member = *it->function->clone(); + member.removePrefix(currentTypePrefix.back()); + symbolTable.insert(member); + } } // Track levels of class/struct/namespace nesting with a prefix string using @@ -7801,11 +7838,10 @@ void HlslParseContext::pushNamespace(const TString& typeName) { // make new type prefix TString newPrefix; - if (currentTypePrefix.size() > 0) { + if (currentTypePrefix.size() > 0) newPrefix = currentTypePrefix.back(); - newPrefix.append(scopeMangler); - } newPrefix.append(typeName); + newPrefix.append(scopeMangler); currentTypePrefix.push_back(newPrefix); } @@ -7823,7 +7859,6 @@ void HlslParseContext::getFullNamespaceName(const TString*& name) const return; TString* fullName = NewPoolTString(currentTypePrefix.back().c_str()); - fullName->append(scopeMangler); fullName->append(*name); name = fullName; } diff --git a/hlsl/hlslParseHelper.h b/hlsl/hlslParseHelper.h index 592cd8293..c981392db 100755 --- a/hlsl/hlslParseHelper.h +++ b/hlsl/hlslParseHelper.h @@ -42,6 +42,7 @@ namespace glslang { class TAttributeMap; // forward declare +class TFunctionDeclarator; class HlslParseContext : public TParseContextBase { public: @@ -93,6 +94,7 @@ public: void decomposeSampleMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments); void decomposeStructBufferMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments); void decomposeGeometryMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments); + void pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments); void addInputArgumentConversions(const TFunction&, TIntermTyped*&); TIntermTyped* addOutputArgumentConversions(const TFunction&, TIntermOperator&); void builtInOpCheck(const TSourceLoc&, const TFunction&, TIntermOperator&); @@ -135,7 +137,7 @@ public: void mergeObjectLayoutQualifiers(TQualifier& dest, const TQualifier& src, bool inheritOnly); void checkNoShaderLayouts(const TSourceLoc&, const TShaderQualifiers&); - const TFunction* findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, TIntermTyped*& args); + const TFunction* findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth, TIntermTyped*& args); void declareTypedef(const TSourceLoc&, const TString& identifier, const TType&); void declareStruct(const TSourceLoc&, TString& structName, TType&); TSymbol* lookupUserType(const TString&, TType&); @@ -166,7 +168,7 @@ public: void pushScope() { symbolTable.push(); } void popScope() { symbolTable.pop(0); } - void pushThisScope(const TType&); + void pushThisScope(const TType&, const TVector&); void popThisScope() { symbolTable.pop(0); } void pushImplicitThis(TVariable* thisParameter) { implicitThisStack.push_back(thisParameter); }