Commit 1ecd14b8 by Olli Etuaho Committed by Commit Bot

Fold user-definedness of function nodes into TOperator

Whether a function call is user-defined is not orthogonal to TOperator associated with the call node - other ops than function calls can't be user-defined. Because of this it makes sense to store the user- definedness by having different TOperator enums for different types of calls. This patch also tags internal helper functions that have a raw definition outside the AST with a separate TOperator enum. This way they can be handled with logic that is easy to understand. Before this, function calls like this left the user-defined bit unset, despite not really being built-ins either. The EmulatePrecision traverser uses this. This is also something that could be used to clean up built-in emulation in the future. BUG=angleproject:1490 TEST=angle_unittests Change-Id: I597fcd9789d0cc22b689ef3ce5a0cc3f621d4859 Reviewed-on: https://chromium-review.googlesource.com/433443Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent 5c32a6ef
...@@ -102,27 +102,23 @@ class PullGradient : public TIntermTraverser ...@@ -102,27 +102,23 @@ class PullGradient : public TIntermTraverser
{ {
if (visit == PreVisit) if (visit == PreVisit)
{ {
if (node->getOp() == EOpFunctionCall) if (node->getOp() == EOpCallFunctionInAST)
{ {
if (node->isUserDefined()) size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
{ ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
if ((*mMetadataList)[calleeIndex].mUsesGradient) if ((*mMetadataList)[calleeIndex].mUsesGradient)
{
onGradient();
}
}
else
{ {
TString name = onGradient();
TFunction::unmangleName(node->getFunctionSymbolInfo()->getName()); }
}
else if (node->getOp() == EOpCallBuiltInFunction)
{
TString name = TFunction::unmangleName(node->getFunctionSymbolInfo()->getName());
if (name == "texture2D" || name == "texture2DProj" || name == "textureCube") if (name == "texture2D" || name == "texture2DProj" || name == "textureCube")
{ {
onGradient(); onGradient();
}
} }
} }
} }
...@@ -273,17 +269,14 @@ class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser ...@@ -273,17 +269,14 @@ class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
bool visitAggregate(Visit visit, TIntermAggregate *node) override bool visitAggregate(Visit visit, TIntermAggregate *node) override
{ {
if (visit == PreVisit && node->getOp() == EOpFunctionCall) if (visit == PreVisit && node->getOp() == EOpCallFunctionInAST)
{ {
if (node->isUserDefined()) size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
{ ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph) if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph)
{ {
onGradientLoop(); onGradientLoop();
}
} }
} }
...@@ -354,8 +347,8 @@ class PushDiscontinuousLoops : public TIntermTraverser ...@@ -354,8 +347,8 @@ class PushDiscontinuousLoops : public TIntermTraverser
{ {
switch (node->getOp()) switch (node->getOp())
{ {
case EOpFunctionCall: case EOpCallFunctionInAST:
if (visit == PreVisit && node->isUserDefined() && mNestedDiscont > 0) if (visit == PreVisit && mNestedDiscont > 0)
{ {
size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo()); size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex); ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
......
...@@ -43,9 +43,8 @@ TIntermSymbol *CreateReturnValueOutSymbol(const TType &type) ...@@ -43,9 +43,8 @@ TIntermSymbol *CreateReturnValueOutSymbol(const TType &type)
TIntermAggregate *CreateReplacementCall(TIntermAggregate *originalCall, TIntermAggregate *CreateReplacementCall(TIntermAggregate *originalCall,
TIntermTyped *returnValueTarget) TIntermTyped *returnValueTarget)
{ {
TIntermAggregate *replacementCall = new TIntermAggregate(EOpFunctionCall); TIntermAggregate *replacementCall = new TIntermAggregate(EOpCallFunctionInAST);
replacementCall->setType(TType(EbtVoid)); replacementCall->setType(TType(EbtVoid));
replacementCall->setUserDefined();
*replacementCall->getFunctionSymbolInfo() = *originalCall->getFunctionSymbolInfo(); *replacementCall->getFunctionSymbolInfo() = *originalCall->getFunctionSymbolInfo();
replacementCall->setLine(originalCall->getLine()); replacementCall->setLine(originalCall->getLine());
TIntermSequence *replacementParameters = replacementCall->getSequence(); TIntermSequence *replacementParameters = replacementCall->getSequence();
...@@ -124,7 +123,8 @@ bool ArrayReturnValueToOutParameterTraverser::visitFunctionPrototype(Visit visit ...@@ -124,7 +123,8 @@ bool ArrayReturnValueToOutParameterTraverser::visitFunctionPrototype(Visit visit
bool ArrayReturnValueToOutParameterTraverser::visitAggregate(Visit visit, TIntermAggregate *node) bool ArrayReturnValueToOutParameterTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
{ {
if (visit == PreVisit && node->isArray() && node->getOp() == EOpFunctionCall) ASSERT(!node->isArray() || node->getOp() != EOpCallInternalRawFunction);
if (visit == PreVisit && node->isArray() && node->getOp() == EOpCallFunctionInAST)
{ {
// Handle call sites where the returned array is not assigned. // Handle call sites where the returned array is not assigned.
// Examples where f() is a function returning an array: // Examples where f() is a function returning an array:
...@@ -181,8 +181,8 @@ bool ArrayReturnValueToOutParameterTraverser::visitBinary(Visit visit, TIntermBi ...@@ -181,8 +181,8 @@ bool ArrayReturnValueToOutParameterTraverser::visitBinary(Visit visit, TIntermBi
if (node->getOp() == EOpAssign && node->getLeft()->isArray()) if (node->getOp() == EOpAssign && node->getLeft()->isArray())
{ {
TIntermAggregate *rightAgg = node->getRight()->getAsAggregate(); TIntermAggregate *rightAgg = node->getRight()->getAsAggregate();
if (rightAgg != nullptr && rightAgg->getOp() == EOpFunctionCall && ASSERT(rightAgg == nullptr || rightAgg->getOp() != EOpCallInternalRawFunction);
rightAgg->isUserDefined()) if (rightAgg != nullptr && rightAgg->getOp() == EOpCallFunctionInAST)
{ {
TIntermAggregate *replacementCall = CreateReplacementCall(rightAgg, node->getLeft()); TIntermAggregate *replacementCall = CreateReplacementCall(rightAgg, node->getLeft());
queueReplacement(node, replacementCall, OriginalNode::IS_DROPPED); queueReplacement(node, replacementCall, OriginalNode::IS_DROPPED);
......
...@@ -38,7 +38,7 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTr ...@@ -38,7 +38,7 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTr
{ {
// Here we handle all the built-in functions mapped to ops, not just the ones that are // Here we handle all the built-in functions mapped to ops, not just the ones that are
// currently identified as problematic. // currently identified as problematic.
if (node->isConstructor() || node->getOp() == EOpFunctionCall) if (node->isConstructor() || node->isFunctionCall())
{ {
return true; return true;
} }
......
...@@ -136,30 +136,17 @@ class CallDAG::CallDAGCreator : public TIntermTraverser ...@@ -136,30 +136,17 @@ class CallDAG::CallDAGCreator : public TIntermTraverser
// Aggregates the AST node for each function as well as the name of the functions called by it // Aggregates the AST node for each function as well as the name of the functions called by it
bool visitAggregate(Visit visit, TIntermAggregate *node) override bool visitAggregate(Visit visit, TIntermAggregate *node) override
{ {
switch (node->getOp()) if (visit == PreVisit && node->getOp() == EOpCallFunctionInAST)
{ {
case EOpFunctionCall: // Function call, add the callees
auto it = mFunctions.find(node->getFunctionSymbolInfo()->getName());
ASSERT(it != mFunctions.end());
// We might be in a top-level function call to set a global variable
if (mCurrentFunction)
{ {
// Function call, add the callees mCurrentFunction->callees.insert(&it->second);
if (visit == PreVisit)
{
// Do not handle calls to builtin functions
if (node->isUserDefined())
{
auto it = mFunctions.find(node->getFunctionSymbolInfo()->getName());
ASSERT(it != mFunctions.end());
// We might be in a top-level function call to set a global variable
if (mCurrentFunction)
{
mCurrentFunction->callees.insert(&it->second);
}
}
}
break;
} }
default:
break;
} }
return true; return true;
} }
......
...@@ -50,9 +50,7 @@ TIntermFunctionDefinition *CreateFunctionDefinitionNode(const char *name, ...@@ -50,9 +50,7 @@ TIntermFunctionDefinition *CreateFunctionDefinitionNode(const char *name,
TIntermAggregate *CreateFunctionCallNode(const char *name, const int functionId) TIntermAggregate *CreateFunctionCallNode(const char *name, const int functionId)
{ {
TIntermAggregate *functionNode = new TIntermAggregate(EOpFunctionCall); TIntermAggregate *functionNode = new TIntermAggregate(EOpCallFunctionInAST);
functionNode->setUserDefined();
SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name); SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
TType returnType(EbtVoid); TType returnType(EbtVoid);
functionNode->setType(returnType); functionNode->setType(returnType);
......
...@@ -429,7 +429,7 @@ bool canRoundFloat(const TType &type) ...@@ -429,7 +429,7 @@ bool canRoundFloat(const TType &type)
TIntermAggregate *createInternalFunctionCallNode(TString name, TIntermNode *child) TIntermAggregate *createInternalFunctionCallNode(TString name, TIntermNode *child)
{ {
TIntermAggregate *callNode = new TIntermAggregate(EOpFunctionCall); TIntermAggregate *callNode = new TIntermAggregate(EOpCallInternalRawFunction);
TName nameObj(TFunction::mangleName(name)); TName nameObj(TFunction::mangleName(name));
nameObj.setInternal(true); nameObj.setInternal(true);
callNode->getFunctionSymbolInfo()->setNameObj(nameObj); callNode->getFunctionSymbolInfo()->setNameObj(nameObj);
...@@ -638,24 +638,11 @@ bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node) ...@@ -638,24 +638,11 @@ bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
switch (node->getOp()) switch (node->getOp())
{ {
case EOpConstructStruct: case EOpConstructStruct:
case EOpCallInternalRawFunction:
case EOpCallFunctionInAST:
// User-defined function return values are not rounded. The calculations that produced
// the value inside the function definition should have been rounded.
break; break;
case EOpFunctionCall:
{
// Function call.
if (visit == PreVisit)
{
// User-defined function return values are not rounded, this relies on that
// calculations producing the value were rounded.
TIntermNode *parent = getParentNode();
if (canRoundFloat(node->getType()) && !isInFunctionMap(node) &&
parentUsesResult(parent, node))
{
TIntermNode *replacement = createRoundingFunctionCallNode(node);
queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
}
}
break;
}
default: default:
TIntermNode *parent = getParentNode(); TIntermNode *parent = getParentNode();
if (canRoundFloat(node->getType()) && visit == PreVisit && if (canRoundFloat(node->getType()) && visit == PreVisit &&
......
...@@ -309,7 +309,7 @@ void TIntermAggregate::setPrecisionFromChildren() ...@@ -309,7 +309,7 @@ void TIntermAggregate::setPrecisionFromChildren()
void TIntermAggregate::setPrecisionForBuiltInOp() void TIntermAggregate::setPrecisionForBuiltInOp()
{ {
ASSERT(!isConstructor()); ASSERT(!isConstructor());
ASSERT(mOp != EOpFunctionCall); ASSERT(!isFunctionCall());
if (!setPrecisionForSpecialBuiltInOp()) if (!setPrecisionForSpecialBuiltInOp())
{ {
setPrecisionFromChildren(); setPrecisionFromChildren();
...@@ -340,7 +340,7 @@ void TIntermAggregate::setBuiltInFunctionPrecision() ...@@ -340,7 +340,7 @@ void TIntermAggregate::setBuiltInFunctionPrecision()
{ {
// All built-ins returning bool should be handled as ops, not functions. // All built-ins returning bool should be handled as ops, not functions.
ASSERT(getBasicType() != EbtBool); ASSERT(getBasicType() != EbtBool);
ASSERT(getOp() == EOpFunctionCall); ASSERT(mOp == EOpCallBuiltInFunction);
TPrecision precision = EbpUndefined; TPrecision precision = EbpUndefined;
TIntermSequence::iterator childIter = mSequence.begin(); TIntermSequence::iterator childIter = mSequence.begin();
...@@ -549,7 +549,6 @@ void TFunctionSymbolInfo::setFromFunction(const TFunction &function) ...@@ -549,7 +549,6 @@ void TFunctionSymbolInfo::setFromFunction(const TFunction &function)
TIntermAggregate::TIntermAggregate(const TIntermAggregate &node) TIntermAggregate::TIntermAggregate(const TIntermAggregate &node)
: TIntermOperator(node), : TIntermOperator(node),
mUserDefined(node.mUserDefined),
mUseEmulatedFunction(node.mUseEmulatedFunction), mUseEmulatedFunction(node.mUseEmulatedFunction),
mGotPrecisionFromChildren(node.mGotPrecisionFromChildren), mGotPrecisionFromChildren(node.mGotPrecisionFromChildren),
mFunctionInfo(node.mFunctionInfo) mFunctionInfo(node.mFunctionInfo)
...@@ -659,6 +658,19 @@ bool TIntermOperator::isConstructor() const ...@@ -659,6 +658,19 @@ bool TIntermOperator::isConstructor() const
} }
} }
bool TIntermOperator::isFunctionCall() const
{
switch (mOp)
{
case EOpCallFunctionInAST:
case EOpCallBuiltInFunction:
case EOpCallInternalRawFunction:
return true;
default:
return false;
}
}
TOperator TIntermBinary::GetMulOpBasedOnOperands(const TType &left, const TType &right) TOperator TIntermBinary::GetMulOpBasedOnOperands(const TType &left, const TType &right)
{ {
if (left.isMatrix()) if (left.isMatrix())
......
...@@ -411,6 +411,10 @@ class TIntermOperator : public TIntermTyped ...@@ -411,6 +411,10 @@ class TIntermOperator : public TIntermTyped
bool isMultiplication() const; bool isMultiplication() const;
bool isConstructor() const; bool isConstructor() const;
// Returns true for calls mapped to EOpCall*, false for built-ins that have their own specific
// ops.
bool isFunctionCall() const;
bool hasSideEffects() const override { return isAssignment(); } bool hasSideEffects() const override { return isAssignment(); }
protected: protected:
...@@ -547,6 +551,7 @@ class TFunctionSymbolInfo ...@@ -547,6 +551,7 @@ class TFunctionSymbolInfo
void setFromFunction(const TFunction &function); void setFromFunction(const TFunction &function);
// The name stored here should always be mangled.
void setNameObj(const TName &name) { mName = name; } void setNameObj(const TName &name) { mName = name; }
const TName &getNameObj() const { return mName; } const TName &getNameObj() const { return mName; }
...@@ -590,7 +595,6 @@ class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase ...@@ -590,7 +595,6 @@ class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase
public: public:
TIntermAggregate(TOperator op) TIntermAggregate(TOperator op)
: TIntermOperator(op), : TIntermOperator(op),
mUserDefined(false),
mUseEmulatedFunction(false), mUseEmulatedFunction(false),
mGotPrecisionFromChildren(false) mGotPrecisionFromChildren(false)
{ {
...@@ -613,9 +617,6 @@ class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase ...@@ -613,9 +617,6 @@ class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase
TIntermSequence *getSequence() override { return &mSequence; } TIntermSequence *getSequence() override { return &mSequence; }
const TIntermSequence *getSequence() const override { return &mSequence; } const TIntermSequence *getSequence() const override { return &mSequence; }
void setUserDefined() { mUserDefined = true; }
bool isUserDefined() const { return mUserDefined; }
void setUseEmulatedFunction() { mUseEmulatedFunction = true; } void setUseEmulatedFunction() { mUseEmulatedFunction = true; }
bool getUseEmulatedFunction() { return mUseEmulatedFunction; } bool getUseEmulatedFunction() { return mUseEmulatedFunction; }
...@@ -625,7 +626,7 @@ class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase ...@@ -625,7 +626,7 @@ class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase
void setPrecisionFromChildren(); void setPrecisionFromChildren();
// Used for built-in functions under EOpFunctionCall. // Used for built-in functions under EOpCallBuiltInFunction.
void setBuiltInFunctionPrecision(); void setBuiltInFunctionPrecision();
// Returns true if changing parameter precision may affect the return value. // Returns true if changing parameter precision may affect the return value.
...@@ -636,10 +637,9 @@ class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase ...@@ -636,10 +637,9 @@ class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase
protected: protected:
TIntermSequence mSequence; TIntermSequence mSequence;
bool mUserDefined; // used for user defined function names
// If set to true, replace the built-in function call with an emulated one // If set to true, replace the built-in function call with an emulated one
// to work around driver bugs. // to work around driver bugs. Only for calls mapped to ops other than EOpCall*.
bool mUseEmulatedFunction; bool mUseEmulatedFunction;
bool mGotPrecisionFromChildren; bool mGotPrecisionFromChildren;
...@@ -1188,10 +1188,6 @@ class TLValueTrackingTraverser : public TIntermTraverser ...@@ -1188,10 +1188,6 @@ class TLValueTrackingTraverser : public TIntermTraverser
return mOperatorRequiresLValue || mInFunctionCallOutParameter; return mOperatorRequiresLValue || mInFunctionCallOutParameter;
} }
// Return true if the prototype or definition of the function being called has been encountered
// during traversal.
bool isInFunctionMap(const TIntermAggregate *callNode) const;
private: private:
// Track whether an l-value is required in the node that is currently being traversed by the // Track whether an l-value is required in the node that is currently being traversed by the
// surrounding operator. // surrounding operator.
...@@ -1205,6 +1201,10 @@ class TLValueTrackingTraverser : public TIntermTraverser ...@@ -1205,6 +1201,10 @@ class TLValueTrackingTraverser : public TIntermTraverser
// Add a function encountered during traversal to the function map. // Add a function encountered during traversal to the function map.
void addToFunctionMap(const TName &name, TIntermSequence *paramSequence); void addToFunctionMap(const TName &name, TIntermSequence *paramSequence);
// Return true if the prototype or definition of the function being called has been encountered
// during traversal.
bool isInFunctionMap(const TIntermAggregate *callNode) const;
// Return the parameters sequence from the function definition or prototype. // Return the parameters sequence from the function definition or prototype.
TIntermSequence *getFunctionParameters(const TIntermAggregate *callNode); TIntermSequence *getFunctionParameters(const TIntermAggregate *callNode);
......
...@@ -87,8 +87,7 @@ bool IntermNodePatternMatcher::match(TIntermAggregate *node, TIntermNode *parent ...@@ -87,8 +87,7 @@ bool IntermNodePatternMatcher::match(TIntermAggregate *node, TIntermNode *parent
(parentBinary->getOp() == EOpAssign || parentBinary->getOp() == EOpInitialize)); (parentBinary->getOp() == EOpAssign || parentBinary->getOp() == EOpInitialize));
if (node->getType().isArray() && !parentIsAssignment && if (node->getType().isArray() && !parentIsAssignment &&
(node->isConstructor() || node->getOp() == EOpFunctionCall) && (node->isConstructor() || node->isFunctionCall()) && !parentNode->getAsBlock())
!parentNode->getAsBlock())
{ {
return true; return true;
} }
......
...@@ -235,7 +235,7 @@ void TLValueTrackingTraverser::addToFunctionMap(const TName &name, TIntermSequen ...@@ -235,7 +235,7 @@ void TLValueTrackingTraverser::addToFunctionMap(const TName &name, TIntermSequen
bool TLValueTrackingTraverser::isInFunctionMap(const TIntermAggregate *callNode) const bool TLValueTrackingTraverser::isInFunctionMap(const TIntermAggregate *callNode) const
{ {
ASSERT(callNode->getOp() == EOpFunctionCall); ASSERT(callNode->getOp() == EOpCallFunctionInAST);
return (mFunctionMap.find(callNode->getFunctionSymbolInfo()->getNameObj()) != return (mFunctionMap.find(callNode->getFunctionSymbolInfo()->getNameObj()) !=
mFunctionMap.end()); mFunctionMap.end());
} }
...@@ -641,36 +641,43 @@ void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node) ...@@ -641,36 +641,43 @@ void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node)
if (visit) if (visit)
{ {
bool inFunctionMap = false; if (node->getOp() == EOpCallFunctionInAST)
if (node->getOp() == EOpFunctionCall)
{ {
inFunctionMap = isInFunctionMap(node); if (isInFunctionMap(node))
if (!inFunctionMap)
{ {
// The function is not user-defined - it is likely built-in texture function. TIntermSequence *params = getFunctionParameters(node);
// Assume that those do not have out parameters. TIntermSequence::iterator paramIter = params->begin();
setInFunctionCallOutParameter(false); for (auto *child : *sequence)
{
ASSERT(paramIter != params->end());
TQualifier qualifier = (*paramIter)->getAsTyped()->getQualifier();
setInFunctionCallOutParameter(qualifier == EvqOut || qualifier == EvqInOut);
child->traverse(this);
if (visit && inVisit)
{
if (child != sequence->back())
visit = visitAggregate(InVisit, node);
}
++paramIter;
}
} }
} else
if (inFunctionMap)
{
TIntermSequence *params = getFunctionParameters(node);
TIntermSequence::iterator paramIter = params->begin();
for (auto *child : *sequence)
{ {
ASSERT(paramIter != params->end()); // The node might not be in the function map in case we're in the middle of
TQualifier qualifier = (*paramIter)->getAsTyped()->getQualifier(); // transforming the AST, and have inserted function call nodes without inserting the
setInFunctionCallOutParameter(qualifier == EvqOut || qualifier == EvqInOut); // function definitions yet.
setInFunctionCallOutParameter(false);
child->traverse(this); for (auto *child : *sequence)
if (visit && inVisit)
{ {
if (child != sequence->back()) child->traverse(this);
visit = visitAggregate(InVisit, node); if (visit && inVisit)
{
if (child != sequence->back())
visit = visitAggregate(InVisit, node);
}
} }
++paramIter;
} }
setInFunctionCallOutParameter(false); setInFunctionCallOutParameter(false);
...@@ -680,7 +687,7 @@ void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node) ...@@ -680,7 +687,7 @@ void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node)
// Find the built-in function corresponding to this op so that we can determine the // Find the built-in function corresponding to this op so that we can determine the
// in/out qualifiers of its parameters. // in/out qualifiers of its parameters.
TFunction *builtInFunc = nullptr; TFunction *builtInFunc = nullptr;
if (!node->isConstructor() && node->getOp() != EOpFunctionCall) if (!node->isFunctionCall() && !node->isConstructor())
{ {
builtInFunc = mSymbolTable.findBuiltInOp(node, mShaderVersion); builtInFunc = mSymbolTable.findBuiltInOp(node, mShaderVersion);
} }
...@@ -689,6 +696,8 @@ void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node) ...@@ -689,6 +696,8 @@ void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node)
for (auto *child : *sequence) for (auto *child : *sequence)
{ {
// This assumes that raw functions called with
// EOpCallInternalRawFunction don't have out parameters.
TQualifier qualifier = EvqIn; TQualifier qualifier = EvqIn;
if (builtInFunc != nullptr) if (builtInFunc != nullptr)
qualifier = builtInFunc->getParam(paramIndex).type->getQualifier(); qualifier = builtInFunc->getParam(paramIndex).type->getQualifier();
......
...@@ -10,7 +10,7 @@ const char *GetOperatorString(TOperator op) ...@@ -10,7 +10,7 @@ const char *GetOperatorString(TOperator op)
{ {
switch (op) switch (op)
{ {
// Note: EOpNull and EOpFunctionCall can't be handled here. // Note: EOpNull and EOpCall* can't be handled here.
case EOpNegative: case EOpNegative:
return "-"; return "-";
......
...@@ -13,7 +13,20 @@ ...@@ -13,7 +13,20 @@
enum TOperator enum TOperator
{ {
EOpNull, // if in a node, should only mean a node is still being built EOpNull, // if in a node, should only mean a node is still being built
EOpFunctionCall,
// Call a function defined in the AST. This might be a user-defined function or a function
// inserted by an AST transformation.
EOpCallFunctionInAST,
// Call an internal helper function with a raw implementation - the implementation can't be
// subject to AST transformations. Raw functions have a few constraints to keep them compatible
// with AST traversers:
// * They should not return arrays.
// * They should not have out parameters.
EOpCallInternalRawFunction,
// Call a built-in function like a texture or image function.
EOpCallBuiltInFunction,
// //
// Unary operators // Unary operators
......
...@@ -877,7 +877,9 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) ...@@ -877,7 +877,9 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node)
TInfoSinkBase &out = objSink(); TInfoSinkBase &out = objSink();
switch (node->getOp()) switch (node->getOp())
{ {
case EOpFunctionCall: case EOpCallFunctionInAST:
case EOpCallInternalRawFunction:
case EOpCallBuiltInFunction:
// Function call. // Function call.
if (visit == PreVisit) if (visit == PreVisit)
out << hashFunctionNameIfNeeded(node->getFunctionSymbolInfo()->getNameObj()) << "("; out << hashFunctionNameIfNeeded(node->getFunctionSymbolInfo()->getNameObj()) << "(";
......
...@@ -1000,7 +1000,7 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) ...@@ -1000,7 +1000,7 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
} }
// ArrayReturnValueToOutParameter should have eliminated expressions where a // ArrayReturnValueToOutParameter should have eliminated expressions where a
// function call is assigned. // function call is assigned.
ASSERT(rightAgg == nullptr || rightAgg->getOp() != EOpFunctionCall); ASSERT(rightAgg == nullptr);
const TString &functionName = addArrayAssignmentFunction(node->getType()); const TString &functionName = addArrayAssignmentFunction(node->getType());
outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
...@@ -1755,12 +1755,14 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) ...@@ -1755,12 +1755,14 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
switch (node->getOp()) switch (node->getOp())
{ {
case EOpFunctionCall: case EOpCallBuiltInFunction:
case EOpCallFunctionInAST:
case EOpCallInternalRawFunction:
{ {
TIntermSequence *arguments = node->getSequence(); TIntermSequence *arguments = node->getSequence();
bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function; bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function;
if (node->isUserDefined()) if (node->getOp() == EOpCallFunctionInAST)
{ {
if (node->isArray()) if (node->isArray())
{ {
...@@ -1774,7 +1776,7 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) ...@@ -1774,7 +1776,7 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
out << DisambiguateFunctionName(node->getSequence()); out << DisambiguateFunctionName(node->getSequence());
out << (lod0 ? "Lod0(" : "("); out << (lod0 ? "Lod0(" : "(");
} }
else if (node->getFunctionSymbolInfo()->getNameObj().isInternal()) else if (node->getOp() == EOpCallInternalRawFunction)
{ {
// This path is used for internal functions that don't have their definitions in the // This path is used for internal functions that don't have their definitions in the
// AST, such as precision emulation functions. // AST, such as precision emulation functions.
......
...@@ -4180,7 +4180,7 @@ TIntermBranch *TParseContext::addBranch(TOperator op, ...@@ -4180,7 +4180,7 @@ TIntermBranch *TParseContext::addBranch(TOperator op,
void TParseContext::checkTextureOffsetConst(TIntermAggregate *functionCall) void TParseContext::checkTextureOffsetConst(TIntermAggregate *functionCall)
{ {
ASSERT(!functionCall->isUserDefined()); ASSERT(functionCall->getOp() == EOpCallBuiltInFunction);
const TString &name = functionCall->getFunctionSymbolInfo()->getName(); const TString &name = functionCall->getFunctionSymbolInfo()->getName();
TIntermNode *offset = nullptr; TIntermNode *offset = nullptr;
TIntermSequence *arguments = functionCall->getSequence(); TIntermSequence *arguments = functionCall->getSequence();
...@@ -4232,7 +4232,7 @@ void TParseContext::checkTextureOffsetConst(TIntermAggregate *functionCall) ...@@ -4232,7 +4232,7 @@ void TParseContext::checkTextureOffsetConst(TIntermAggregate *functionCall)
// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers // GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers
void TParseContext::checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall) void TParseContext::checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall)
{ {
ASSERT(!functionCall->isUserDefined()); ASSERT(functionCall->getOp() == EOpCallBuiltInFunction);
const TString &name = functionCall->getFunctionSymbolInfo()->getName(); const TString &name = functionCall->getFunctionSymbolInfo()->getName();
if (name.compare(0, 5, "image") == 0) if (name.compare(0, 5, "image") == 0)
...@@ -4269,7 +4269,7 @@ void TParseContext::checkImageMemoryAccessForUserDefinedFunctions( ...@@ -4269,7 +4269,7 @@ void TParseContext::checkImageMemoryAccessForUserDefinedFunctions(
const TFunction *functionDefinition, const TFunction *functionDefinition,
const TIntermAggregate *functionCall) const TIntermAggregate *functionCall)
{ {
ASSERT(functionCall->isUserDefined()); ASSERT(functionCall->getOp() == EOpCallFunctionInAST);
const TIntermSequence &arguments = *functionCall->getSequence(); const TIntermSequence &arguments = *functionCall->getSequence();
...@@ -4463,29 +4463,24 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, ...@@ -4463,29 +4463,24 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall,
{ {
// This is a real function call // This is a real function call
ASSERT(argumentsNode->getOp() == EOpNull); ASSERT(argumentsNode->getOp() == EOpNull);
argumentsNode->setOp(EOpFunctionCall);
argumentsNode->setType(fnCandidate->getReturnType()); argumentsNode->setType(fnCandidate->getReturnType());
// this is how we know whether the given function is a builtIn function or a user
// defined function
// if builtIn == false, it's a userDefined -> could be an overloaded
// builtIn function also
// if builtIn == true, it's definitely a builtIn function with EOpNull
if (!builtIn)
argumentsNode->setUserDefined();
argumentsNode->getFunctionSymbolInfo()->setFromFunction(*fnCandidate); argumentsNode->getFunctionSymbolInfo()->setFromFunction(*fnCandidate);
// This needs to happen after the function info including name is set // If builtIn == false, the function is user defined - could be an overloaded
// built-in as well.
// if builtIn == true, it's a builtIn function with no op associated with it.
// This needs to happen after the function info including name is set.
if (builtIn) if (builtIn)
{ {
argumentsNode->setOp(EOpCallBuiltInFunction);
argumentsNode->setBuiltInFunctionPrecision(); argumentsNode->setBuiltInFunctionPrecision();
checkTextureOffsetConst(argumentsNode); checkTextureOffsetConst(argumentsNode);
checkImageMemoryAccessForBuiltinFunctions(argumentsNode); checkImageMemoryAccessForBuiltinFunctions(argumentsNode);
} }
else else
{ {
argumentsNode->setOp(EOpCallFunctionInAST);
checkImageMemoryAccessForUserDefinedFunctions(fnCandidate, argumentsNode); checkImageMemoryAccessForUserDefinedFunctions(fnCandidate, argumentsNode);
} }
......
...@@ -55,6 +55,8 @@ TName GetIndexFunctionName(const TType &type, bool write) ...@@ -55,6 +55,8 @@ TName GetIndexFunctionName(const TType &type, bool write)
} }
TString nameString = TFunction::mangleName(nameSink.c_str()); TString nameString = TFunction::mangleName(nameSink.c_str());
TName name(nameString); TName name(nameString);
// TODO(oetuaho@nvidia.com): would be better to have the parameter types in the mangled name as
// well.
name.setInternal(true); name.setInternal(true);
return name; return name;
} }
...@@ -349,9 +351,8 @@ TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node, ...@@ -349,9 +351,8 @@ TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
TIntermTyped *index) TIntermTyped *index)
{ {
ASSERT(node->getOp() == EOpIndexIndirect); ASSERT(node->getOp() == EOpIndexIndirect);
TIntermAggregate *indexingCall = new TIntermAggregate(EOpFunctionCall); TIntermAggregate *indexingCall = new TIntermAggregate(EOpCallFunctionInAST);
indexingCall->setLine(node->getLine()); indexingCall->setLine(node->getLine());
indexingCall->setUserDefined();
indexingCall->getFunctionSymbolInfo()->setNameObj( indexingCall->getFunctionSymbolInfo()->setNameObj(
GetIndexFunctionName(indexedNode->getType(), false)); GetIndexFunctionName(indexedNode->getType(), false));
indexingCall->getSequence()->push_back(indexedNode); indexingCall->getSequence()->push_back(indexedNode);
...@@ -520,6 +521,11 @@ void RemoveDynamicIndexing(TIntermNode *root, ...@@ -520,6 +521,11 @@ void RemoveDynamicIndexing(TIntermNode *root,
root->traverse(&traverser); root->traverse(&traverser);
traverser.updateTree(); traverser.updateTree();
} while (traverser.usedTreeInsertion()); } while (traverser.usedTreeInsertion());
// TOOD(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
// of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
// function call nodes with no corresponding definition nodes. This needs special handling in
// TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
// superficial reading of the code.
traverser.insertHelperDefinitions(root); traverser.insertHelperDefinitions(root);
traverser.updateTree(); traverser.updateTree();
} }
......
...@@ -66,7 +66,7 @@ bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node) ...@@ -66,7 +66,7 @@ bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node)
} }
// Decide if the node represents the call of texelFetchOffset. // Decide if the node represents the call of texelFetchOffset.
if (node->getOp() != EOpFunctionCall || node->isUserDefined()) if (node->getOp() != EOpCallBuiltInFunction)
{ {
return true; return true;
} }
...@@ -94,7 +94,7 @@ bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node) ...@@ -94,7 +94,7 @@ bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node)
// Create new node that represents the call of function texelFetch. // Create new node that represents the call of function texelFetch.
// Its argument list will be: texelFetch(sampler, Position+offset, lod). // Its argument list will be: texelFetch(sampler, Position+offset, lod).
TIntermAggregate *texelFetchNode = new TIntermAggregate(EOpFunctionCall); TIntermAggregate *texelFetchNode = new TIntermAggregate(EOpCallBuiltInFunction);
texelFetchNode->getFunctionSymbolInfo()->setName(newName); texelFetchNode->getFunctionSymbolInfo()->setName(newName);
texelFetchNode->getFunctionSymbolInfo()->setId(uniqueId); texelFetchNode->getFunctionSymbolInfo()->setId(uniqueId);
texelFetchNode->setType(node->getType()); texelFetchNode->setType(node->getType());
......
...@@ -63,10 +63,6 @@ TIntermAggregate *CopyAggregateNode(TIntermAggregate *node) ...@@ -63,10 +63,6 @@ TIntermAggregate *CopyAggregateNode(TIntermAggregate *node)
copySeq->insert(copySeq->begin(), node->getSequence()->begin(), node->getSequence()->end()); copySeq->insert(copySeq->begin(), node->getSequence()->begin(), node->getSequence()->end());
copyNode->setType(node->getType()); copyNode->setType(node->getType());
*copyNode->getFunctionSymbolInfo() = *node->getFunctionSymbolInfo(); *copyNode->getFunctionSymbolInfo() = *node->getFunctionSymbolInfo();
if (node->isUserDefined())
{
copyNode->setUserDefined();
}
return copyNode; return copyNode;
} }
...@@ -104,7 +100,7 @@ bool SeparateExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate ...@@ -104,7 +100,7 @@ bool SeparateExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate
if (!mPatternToSeparateMatcher.match(node, getParentNode())) if (!mPatternToSeparateMatcher.match(node, getParentNode()))
return true; return true;
ASSERT(node->isConstructor() || node->getOp() == EOpFunctionCall); ASSERT(node->isConstructor() || node->getOp() == EOpCallFunctionInAST);
mFoundArrayExpression = true; mFoundArrayExpression = true;
......
...@@ -158,7 +158,7 @@ TSymbol *TSymbolTable::findBuiltIn(const TString &name, int shaderVersion) const ...@@ -158,7 +158,7 @@ TSymbol *TSymbolTable::findBuiltIn(const TString &name, int shaderVersion) const
TFunction *TSymbolTable::findBuiltInOp(TIntermAggregate *callNode, int shaderVersion) const TFunction *TSymbolTable::findBuiltInOp(TIntermAggregate *callNode, int shaderVersion) const
{ {
ASSERT(!callNode->isConstructor()); ASSERT(!callNode->isConstructor());
ASSERT(callNode->getOp() != EOpFunctionCall); ASSERT(!callNode->isFunctionCall());
TString opString = GetOperatorString(callNode->getOp()); TString opString = GetOperatorString(callNode->getOp());
// The return type doesn't affect the mangled name of the function, which is used to look it up. // The return type doesn't affect the mangled name of the function, which is used to look it up.
TType dummyReturnType; TType dummyReturnType;
......
...@@ -72,8 +72,8 @@ bool ValidateGlobalInitializerTraverser::visitAggregate(Visit visit, TIntermAggr ...@@ -72,8 +72,8 @@ bool ValidateGlobalInitializerTraverser::visitAggregate(Visit visit, TIntermAggr
// Disallow calls to user-defined functions and texture lookup functions in global variable // Disallow calls to user-defined functions and texture lookup functions in global variable
// initializers. // initializers.
// This is done simply by disabling all function calls - built-in math functions don't use // This is done simply by disabling all function calls - built-in math functions don't use
// EOpFunctionCall. // the function call ops.
if (node->getOp() == EOpFunctionCall) if (node->isFunctionCall())
{ {
mIsValid = false; mIsValid = false;
} }
......
...@@ -108,7 +108,8 @@ bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate *node) ...@@ -108,7 +108,8 @@ bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate *node)
{ {
switch (node->getOp()) switch (node->getOp())
{ {
case EOpFunctionCall: case EOpCallFunctionInAST:
case EOpCallBuiltInFunction:
validateFunctionCall(node); validateFunctionCall(node);
break; break;
default: default:
...@@ -378,7 +379,7 @@ bool ValidateLimitations::validateForLoopExpr(TIntermLoop *node, int indexSymbol ...@@ -378,7 +379,7 @@ bool ValidateLimitations::validateForLoopExpr(TIntermLoop *node, int indexSymbol
bool ValidateLimitations::validateFunctionCall(TIntermAggregate *node) bool ValidateLimitations::validateFunctionCall(TIntermAggregate *node)
{ {
ASSERT(node->getOp() == EOpFunctionCall); ASSERT(node->getOp() == EOpCallFunctionInAST || node->getOp() == EOpCallBuiltInFunction);
// If not within loop body, there is nothing to check. // If not within loop body, there is nothing to check.
if (!withinLoopBody()) if (!withinLoopBody())
......
...@@ -354,27 +354,24 @@ bool ValidateMultiviewTraverser::visitAggregate(Visit visit, TIntermAggregate *n ...@@ -354,27 +354,24 @@ bool ValidateMultiviewTraverser::visitAggregate(Visit visit, TIntermAggregate *n
{ {
// Check if the node is an user-defined function call or if an l-value is required, or if // Check if the node is an user-defined function call or if an l-value is required, or if
// there are possible visible side effects, such as image writes. // there are possible visible side effects, such as image writes.
if (node->getOp() == EOpFunctionCall) if (node->getOp() == EOpCallFunctionInAST)
{ {
if (node->isUserDefined()) mDiagnostics->error(node->getLine(),
{ "Disallowed user defined function call inside assignment to "
mDiagnostics->error(node->getLine(), "gl_Position.x when using OVR_multiview",
"Disallowed user defined function call inside assignment to " GetOperatorString(node->getOp()));
"gl_Position.x when using OVR_multiview", mValid = false;
GetOperatorString(node->getOp())); }
mValid = false; else if (node->getOp() == EOpCallBuiltInFunction &&
} TFunction::unmangleName(node->getFunctionSymbolInfo()->getName()) == "imageStore")
else if (TFunction::unmangleName(node->getFunctionSymbolInfo()->getName()) == {
"imageStore") // TODO(oetuaho@nvidia.com): Record which built-in functions have side effects in
{ // the symbol info instead.
// TODO(oetuaho@nvidia.com): Record which built-in functions have side effects in mDiagnostics->error(node->getLine(),
// the symbol info instead. "Disallowed function call with side effects inside assignment "
mDiagnostics->error(node->getLine(), "to gl_Position.x when using OVR_multiview",
"Disallowed function call with side effects inside assignment " GetOperatorString(node->getOp()));
"to gl_Position.x when using OVR_multiview", mValid = false;
GetOperatorString(node->getOp()));
mValid = false;
}
} }
else if (!node->isConstructor()) else if (!node->isConstructor())
{ {
......
...@@ -403,8 +403,15 @@ bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate *node) ...@@ -403,8 +403,15 @@ bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
// mostly use GLSL names for functions. // mostly use GLSL names for functions.
switch (node->getOp()) switch (node->getOp())
{ {
case EOpFunctionCall: case EOpCallFunctionInAST:
OutputFunction(out, "Function Call", node->getFunctionSymbolInfo()); OutputFunction(out, "Call an user-defined function", node->getFunctionSymbolInfo());
break;
case EOpCallInternalRawFunction:
OutputFunction(out, "Call an internal function with raw implementation",
node->getFunctionSymbolInfo());
break;
case EOpCallBuiltInFunction:
OutputFunction(out, "Call a built-in function", node->getFunctionSymbolInfo());
break; break;
case EOpEqualComponentWise: case EOpEqualComponentWise:
......
...@@ -27,8 +27,7 @@ class FunctionCallFinder : public TIntermTraverser ...@@ -27,8 +27,7 @@ class FunctionCallFinder : public TIntermTraverser
bool visitAggregate(Visit visit, TIntermAggregate *node) override bool visitAggregate(Visit visit, TIntermAggregate *node) override
{ {
if (node->getOp() == EOpFunctionCall && if (node->isFunctionCall() && node->getFunctionSymbolInfo()->getName() == mFunctionName)
node->getFunctionSymbolInfo()->getName() == mFunctionName)
{ {
mNodeFound = node; mNodeFound = node;
return false; return false;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment