Commit 7c3848e5 by Olli Etuaho

Allow constant folding some non-constant expressions

This requires removing the assumption that constant folding implies constness in the constant expression sense from various places in the code. This particularly benefits ternary operators, which can now be simplified if just the condition is a compile-time constant. In the future, the groundwork that is laid here could be used to implement more aggressive constant folding of user-defined functions for example. TEST=angle_unittests BUG=angleproject:851 Change-Id: I0eede806570d56746c3dad1e01aa89a91d66013d Reviewed-on: https://chromium-review.googlesource.com/310750Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarZhenyao Mo <zmo@chromium.org>
parent 0ed0d8ac
...@@ -188,14 +188,16 @@ float VectorDotProduct(TConstantUnion *paramArray1, TConstantUnion *paramArray2, ...@@ -188,14 +188,16 @@ float VectorDotProduct(TConstantUnion *paramArray1, TConstantUnion *paramArray2,
return result; return result;
} }
TIntermTyped *CreateFoldedNode(TConstantUnion *constArray, const TIntermTyped *originalNode) TIntermTyped *CreateFoldedNode(TConstantUnion *constArray,
const TIntermTyped *originalNode,
TQualifier qualifier)
{ {
if (constArray == nullptr) if (constArray == nullptr)
{ {
return nullptr; return nullptr;
} }
TIntermTyped *folded = new TIntermConstantUnion(constArray, originalNode->getType()); TIntermTyped *folded = new TIntermConstantUnion(constArray, originalNode->getType());
folded->getTypePointer()->setQualifier(EvqConst); folded->getTypePointer()->setQualifier(qualifier);
folded->setLine(originalNode->getLine()); folded->setLine(originalNode->getLine());
return folded; return folded;
} }
...@@ -893,7 +895,14 @@ TIntermTyped *TIntermBinary::fold(TInfoSink &infoSink) ...@@ -893,7 +895,14 @@ TIntermTyped *TIntermBinary::fold(TInfoSink &infoSink)
return nullptr; return nullptr;
} }
TConstantUnion *constArray = leftConstant->foldBinary(mOp, rightConstant, infoSink); TConstantUnion *constArray = leftConstant->foldBinary(mOp, rightConstant, infoSink);
return CreateFoldedNode(constArray, this);
// Nodes may be constant folded without being qualified as constant.
TQualifier resultQualifier = EvqConst;
if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst)
{
resultQualifier = EvqTemporary;
}
return CreateFoldedNode(constArray, this, resultQualifier);
} }
TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink) TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink)
...@@ -925,7 +934,10 @@ TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink) ...@@ -925,7 +934,10 @@ TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink)
constArray = operandConstant->foldUnaryWithSameReturnType(mOp, infoSink); constArray = operandConstant->foldUnaryWithSameReturnType(mOp, infoSink);
break; break;
} }
return CreateFoldedNode(constArray, this);
// Nodes may be constant folded without being qualified as constant.
TQualifier resultQualifier = mOperand->getQualifier() == EvqConst ? EvqConst : EvqTemporary;
return CreateFoldedNode(constArray, this, resultQualifier);
} }
TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink) TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink)
...@@ -939,7 +951,10 @@ TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink) ...@@ -939,7 +951,10 @@ TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink)
} }
} }
TConstantUnion *constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, infoSink); TConstantUnion *constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, infoSink);
return CreateFoldedNode(constArray, this);
// Nodes may be constant folded without being qualified as constant.
TQualifier resultQualifier = areChildrenConstQualified() ? EvqConst : EvqTemporary;
return CreateFoldedNode(constArray, this, resultQualifier);
} }
// //
......
...@@ -294,6 +294,12 @@ class TIntermRaw : public TIntermTyped ...@@ -294,6 +294,12 @@ class TIntermRaw : public TIntermTyped
TString mRawText; TString mRawText;
}; };
// Constant folded node.
// Note that nodes may be constant folded and not be constant expressions with the EvqConst
// qualifier. This happens for example when the following expression is processed:
// "true ? 1.0 : non_constant"
// Other nodes than TIntermConstantUnion may also be constant expressions.
//
class TIntermConstantUnion : public TIntermTyped class TIntermConstantUnion : public TIntermTyped
{ {
public: public:
......
...@@ -260,7 +260,7 @@ TIntermNode *TIntermediate::addSelection( ...@@ -260,7 +260,7 @@ TIntermNode *TIntermediate::addSelection(
// test now. // test now.
// //
if (cond->getAsTyped() && cond->getAsTyped()->getAsConstantUnion()) if (cond->getAsConstantUnion())
{ {
if (cond->getAsConstantUnion()->getBConst(0) == true) if (cond->getAsConstantUnion()->getBConst(0) == true)
{ {
...@@ -325,19 +325,20 @@ TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *true ...@@ -325,19 +325,20 @@ TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *true
{ {
resultQualifier = EvqConst; resultQualifier = EvqConst;
} }
// Right now it's safe to fold ternary operators only when all operands // Note that the node resulting from here can be a constant union without being qualified as
// are constant. If only the condition is constant, it's theoretically // constant.
// possible to fold the ternary operator, but that requires making sure if (cond->getAsConstantUnion())
// that the node returned from here won't be treated as a constant
// expression in case the node that gets eliminated was not a constant
// expression.
if (resultQualifier == EvqConst && cond->getAsConstantUnion() &&
trueBlock->getAsConstantUnion() && falseBlock->getAsConstantUnion())
{ {
if (cond->getAsConstantUnion()->getBConst(0)) if (cond->getAsConstantUnion()->getBConst(0))
{
trueBlock->getTypePointer()->setQualifier(resultQualifier);
return trueBlock; return trueBlock;
}
else else
{
falseBlock->getTypePointer()->setQualifier(resultQualifier);
return falseBlock; return falseBlock;
}
} }
// //
......
...@@ -164,6 +164,23 @@ void TParseContext::warning(const TSourceLoc &loc, ...@@ -164,6 +164,23 @@ void TParseContext::warning(const TSourceLoc &loc,
mDiagnostics.writeInfo(pp::Diagnostics::PP_WARNING, srcLoc, reason, token, extraInfo); mDiagnostics.writeInfo(pp::Diagnostics::PP_WARNING, srcLoc, reason, token, extraInfo);
} }
void TParseContext::outOfRangeError(bool isError,
const TSourceLoc &loc,
const char *reason,
const char *token,
const char *extraInfo)
{
if (isError)
{
error(loc, reason, token, extraInfo);
recover();
}
else
{
warning(loc, reason, token, extraInfo);
}
}
// //
// Same error message for all places assignments don't work. // Same error message for all places assignments don't work.
// //
...@@ -735,7 +752,10 @@ bool TParseContext::arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped *ex ...@@ -735,7 +752,10 @@ bool TParseContext::arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped *ex
{ {
TIntermConstantUnion *constant = expr->getAsConstantUnion(); TIntermConstantUnion *constant = expr->getAsConstantUnion();
if (constant == nullptr || !constant->isScalarInt()) // TODO(oetuaho@nvidia.com): Get rid of the constant == nullptr check here once all constant
// expressions can be folded. Right now we don't allow constant expressions that ANGLE can't
// fold as array size.
if (expr->getQualifier() != EvqConst || constant == nullptr || !constant->isScalarInt())
{ {
error(line, "array size must be a constant integer expression", ""); error(line, "array size must be a constant integer expression", "");
size = 1; size = 1;
...@@ -1188,11 +1208,10 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location, ...@@ -1188,11 +1208,10 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location,
{ {
const TVariable *variable = getNamedVariable(location, name, symbol); const TVariable *variable = getNamedVariable(location, name, symbol);
if (variable->getType().getQualifier() == EvqConst && variable->getConstPointer()) if (variable->getConstPointer())
{ {
TConstantUnion *constArray = variable->getConstPointer(); TConstantUnion *constArray = variable->getConstPointer();
TType t(variable->getType()); return intermediate.addConstantUnion(constArray, variable->getType(), location);
return intermediate.addConstantUnion(constArray, t, location);
} }
else else
{ {
...@@ -1350,24 +1369,15 @@ bool TParseContext::executeInitializer(const TSourceLoc &line, ...@@ -1350,24 +1369,15 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
return false; return false;
} }
bool TParseContext::areAllChildConst(TIntermAggregate *aggrNode) bool TParseContext::areAllChildrenConstantFolded(TIntermAggregate *aggrNode)
{ {
ASSERT(aggrNode != NULL); ASSERT(aggrNode != nullptr);
if (!aggrNode->isConstructor()) for (TIntermNode *&node : *aggrNode->getSequence())
return false;
bool allConstant = true;
// check if all the child nodes are constants so that they can be inserted into
// the parent node
TIntermSequence *sequence = aggrNode->getSequence();
for (TIntermSequence::iterator p = sequence->begin(); p != sequence->end(); ++p)
{ {
if (!(*p)->getAsTyped()->getAsConstantUnion()) if (node->getAsConstantUnion() == nullptr)
return false; return false;
} }
return true;
return allConstant;
} }
TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier, TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier,
...@@ -2295,11 +2305,10 @@ TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments, ...@@ -2295,11 +2305,10 @@ TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments,
// Turn the argument list itself into a constructor // Turn the argument list itself into a constructor
TIntermAggregate *constructor = intermediate.setAggregateOperator(aggregateArguments, op, line); TIntermAggregate *constructor = intermediate.setAggregateOperator(aggregateArguments, op, line);
TIntermTyped *constConstructor = foldConstConstructor(constructor, *type); ASSERT(constructor->isConstructor());
if (constConstructor)
{ // Need to set type before setPrecisionFromChildren() because bool doesn't have precision.
return constConstructor; constructor->setType(*type);
}
// Structs should not be precision qualified, the individual members may be. // Structs should not be precision qualified, the individual members may be.
// Built-in types on the other hand should be precision qualified. // Built-in types on the other hand should be precision qualified.
...@@ -2309,14 +2318,19 @@ TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments, ...@@ -2309,14 +2318,19 @@ TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments,
type->setPrecision(constructor->getPrecision()); type->setPrecision(constructor->getPrecision());
} }
TIntermTyped *constConstructor = foldConstConstructor(constructor, *type);
if (constConstructor)
{
return constConstructor;
}
return constructor; return constructor;
} }
TIntermTyped *TParseContext::foldConstConstructor(TIntermAggregate *aggrNode, const TType &type) TIntermTyped *TParseContext::foldConstConstructor(TIntermAggregate *aggrNode, const TType &type)
{ {
// TODO: Add support for folding array constructors // TODO: Add support for folding array constructors
bool canBeFolded = areAllChildConst(aggrNode) && !type.isArray(); bool canBeFolded = areAllChildrenConstantFolded(aggrNode) && !type.isArray();
aggrNode->setType(type);
if (canBeFolded) if (canBeFolded)
{ {
bool returnVal = false; bool returnVal = false;
...@@ -2345,36 +2359,16 @@ TIntermTyped *TParseContext::foldConstConstructor(TIntermAggregate *aggrNode, co ...@@ -2345,36 +2359,16 @@ TIntermTyped *TParseContext::foldConstConstructor(TIntermAggregate *aggrNode, co
// vector. // vector.
// If only one component of vector is accessed (v.x or v[0] where v is a contant vector), then a // If only one component of vector is accessed (v.x or v[0] where v is a contant vector), then a
// contant node is returned, else an aggregate node is returned (for v.xy). The input to this // contant node is returned, else an aggregate node is returned (for v.xy). The input to this
// function could either // function could either be the symbol node or it could be the intermediate tree representation of
// be the symbol node or it could be the intermediate tree representation of accessing fields in a // accessing fields in a constant structure or column of a constant matrix.
// constant
// structure or column of a constant matrix.
// //
TIntermTyped *TParseContext::addConstVectorNode(TVectorFields &fields, TIntermTyped *TParseContext::addConstVectorNode(TVectorFields &fields,
TIntermTyped *node, TIntermConstantUnion *node,
const TSourceLoc &line) const TSourceLoc &line,
bool outOfRangeIndexIsError)
{ {
TIntermTyped *typedNode; const TConstantUnion *unionArray = node->getUnionArrayPointer();
TIntermConstantUnion *tempConstantNode = node->getAsConstantUnion(); ASSERT(unionArray);
const TConstantUnion *unionArray;
if (tempConstantNode)
{
unionArray = tempConstantNode->getUnionArrayPointer();
if (!unionArray)
{
return node;
}
}
else
{ // The node has to be either a symbol node or an aggregate node or a tempConstant node, else,
// its an error
error(line, "Cannot offset into the vector", "Error");
recover();
return 0;
}
TConstantUnion *constArray = new TConstantUnion[fields.num]; TConstantUnion *constArray = new TConstantUnion[fields.num];
...@@ -2385,59 +2379,39 @@ TIntermTyped *TParseContext::addConstVectorNode(TVectorFields &fields, ...@@ -2385,59 +2379,39 @@ TIntermTyped *TParseContext::addConstVectorNode(TVectorFields &fields,
std::stringstream extraInfoStream; std::stringstream extraInfoStream;
extraInfoStream << "vector field selection out of range '" << fields.offsets[i] << "'"; extraInfoStream << "vector field selection out of range '" << fields.offsets[i] << "'";
std::string extraInfo = extraInfoStream.str(); std::string extraInfo = extraInfoStream.str();
error(line, "", "[", extraInfo.c_str()); outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str());
recover(); fields.offsets[i] = node->getType().getNominalSize() - 1;
fields.offsets[i] = 0;
} }
constArray[i] = unionArray[fields.offsets[i]]; constArray[i] = unionArray[fields.offsets[i]];
} }
typedNode = intermediate.addConstantUnion(constArray, node->getType(), line); return intermediate.addConstantUnion(constArray, node->getType(), line);
return typedNode;
} }
// //
// This function returns the column being accessed from a constant matrix. The values are retrieved // This function returns the column being accessed from a constant matrix. The values are retrieved
// from the symbol table and parse-tree is built for a vector (each column of a matrix is a vector). // from the symbol table and parse-tree is built for a vector (each column of a matrix is a vector).
// The // The input to the function could either be a symbol node (m[0] where m is a constant matrix)that
// input to the function could either be a symbol node (m[0] where m is a constant matrix)that // represents a constant matrix or it could be the tree representation of the constant matrix
// represents // (s.m1[0] where s is a constant structure)
// a constant matrix or it could be the tree representation of the constant matrix (s.m1[0] where s
// is a constant structure)
// //
TIntermTyped *TParseContext::addConstMatrixNode(int index, TIntermTyped *TParseContext::addConstMatrixNode(int index,
TIntermTyped *node, TIntermConstantUnion *node,
const TSourceLoc &line) const TSourceLoc &line,
bool outOfRangeIndexIsError)
{ {
TIntermTyped *typedNode;
TIntermConstantUnion *tempConstantNode = node->getAsConstantUnion();
if (index >= node->getType().getCols()) if (index >= node->getType().getCols())
{ {
std::stringstream extraInfoStream; std::stringstream extraInfoStream;
extraInfoStream << "matrix field selection out of range '" << index << "'"; extraInfoStream << "matrix field selection out of range '" << index << "'";
std::string extraInfo = extraInfoStream.str(); std::string extraInfo = extraInfoStream.str();
error(line, "", "[", extraInfo.c_str()); outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str());
recover(); index = node->getType().getCols() - 1;
index = 0;
}
if (tempConstantNode)
{
TConstantUnion *unionArray = tempConstantNode->getUnionArrayPointer();
int size = tempConstantNode->getType().getCols();
typedNode = intermediate.addConstantUnion(&unionArray[size * index],
tempConstantNode->getType(), line);
}
else
{
error(line, "Cannot offset into the matrix", "Error");
recover();
return 0;
} }
return typedNode; TConstantUnion *unionArray = node->getUnionArrayPointer();
int size = node->getType().getCols();
return intermediate.addConstantUnion(&unionArray[size * index], node->getType(), line);
} }
// //
...@@ -2448,11 +2422,10 @@ TIntermTyped *TParseContext::addConstMatrixNode(int index, ...@@ -2448,11 +2422,10 @@ TIntermTyped *TParseContext::addConstMatrixNode(int index,
// constant structure) // constant structure)
// //
TIntermTyped *TParseContext::addConstArrayNode(int index, TIntermTyped *TParseContext::addConstArrayNode(int index,
TIntermTyped *node, TIntermConstantUnion *node,
const TSourceLoc &line) const TSourceLoc &line,
bool outOfRangeIndexIsError)
{ {
TIntermTyped *typedNode;
TIntermConstantUnion *tempConstantNode = node->getAsConstantUnion();
TType arrayElementType = node->getType(); TType arrayElementType = node->getType();
arrayElementType.clearArrayness(); arrayElementType.clearArrayness();
...@@ -2461,27 +2434,13 @@ TIntermTyped *TParseContext::addConstArrayNode(int index, ...@@ -2461,27 +2434,13 @@ TIntermTyped *TParseContext::addConstArrayNode(int index,
std::stringstream extraInfoStream; std::stringstream extraInfoStream;
extraInfoStream << "array field selection out of range '" << index << "'"; extraInfoStream << "array field selection out of range '" << index << "'";
std::string extraInfo = extraInfoStream.str(); std::string extraInfo = extraInfoStream.str();
error(line, "", "[", extraInfo.c_str()); outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str());
recover(); index = node->getType().getArraySize() - 1;
index = 0;
}
if (tempConstantNode)
{
size_t arrayElementSize = arrayElementType.getObjectSize();
TConstantUnion *unionArray = tempConstantNode->getUnionArrayPointer();
typedNode = intermediate.addConstantUnion(&unionArray[arrayElementSize * index],
tempConstantNode->getType(), line);
} }
else size_t arrayElementSize = arrayElementType.getObjectSize();
{ TConstantUnion *unionArray = node->getUnionArrayPointer();
error(line, "Cannot offset into the array", "Error"); return intermediate.addConstantUnion(&unionArray[arrayElementSize * index], node->getType(),
recover(); line);
return 0;
}
return typedNode;
} }
// //
...@@ -2775,25 +2734,31 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, ...@@ -2775,25 +2734,31 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion(); TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion();
if (indexExpression->getQualifier() == EvqConst && indexConstantUnion) if (indexConstantUnion)
{ {
// If the index is not qualified as constant, the behavior in the spec is undefined. This
// applies even if ANGLE has been able to constant fold it (ANGLE may constant fold
// expressions that are not constant expressions). The most compatible way to handle this
// case is to report a warning instead of an error and force the index to be in the
// correct range.
bool outOfRangeIndexIsError = indexExpression->getQualifier() == EvqConst;
int index = indexConstantUnion->getIConst(0); int index = indexConstantUnion->getIConst(0);
if (index < 0) if (index < 0)
{ {
std::stringstream infoStream; std::stringstream infoStream;
infoStream << index; infoStream << index;
std::string info = infoStream.str(); std::string info = infoStream.str();
error(location, "negative index", info.c_str()); outOfRangeError(outOfRangeIndexIsError, location, "negative index", info.c_str());
recover();
index = 0; index = 0;
} }
if (baseExpression->getType().getQualifier() == EvqConst && TIntermConstantUnion *baseConstantUnion = baseExpression->getAsConstantUnion();
baseExpression->getAsConstantUnion()) if (baseConstantUnion)
{ {
if (baseExpression->isArray()) if (baseExpression->isArray())
{ {
// constant folding for array indexing // constant folding for array indexing
indexedExpression = addConstArrayNode(index, baseExpression, location); indexedExpression =
addConstArrayNode(index, baseConstantUnion, location, outOfRangeIndexIsError);
} }
else if (baseExpression->isVector()) else if (baseExpression->isVector())
{ {
...@@ -2802,12 +2767,14 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, ...@@ -2802,12 +2767,14 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
fields.num = 1; fields.num = 1;
fields.offsets[0] = fields.offsets[0] =
index; // need to do it this way because v.xy sends fields integer array index; // need to do it this way because v.xy sends fields integer array
indexedExpression = addConstVectorNode(fields, baseExpression, location); indexedExpression =
addConstVectorNode(fields, baseConstantUnion, location, outOfRangeIndexIsError);
} }
else if (baseExpression->isMatrix()) else if (baseExpression->isMatrix())
{ {
// constant folding for matrix indexing // constant folding for matrix indexing
indexedExpression = addConstMatrixNode(index, baseExpression, location); indexedExpression =
addConstMatrixNode(index, baseConstantUnion, location, outOfRangeIndexIsError);
} }
} }
else else
...@@ -2821,17 +2788,16 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, ...@@ -2821,17 +2788,16 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
std::stringstream extraInfoStream; std::stringstream extraInfoStream;
extraInfoStream << "array index out of range '" << index << "'"; extraInfoStream << "array index out of range '" << index << "'";
std::string extraInfo = extraInfoStream.str(); std::string extraInfo = extraInfoStream.str();
error(location, "", "[", extraInfo.c_str()); outOfRangeError(outOfRangeIndexIsError, location, "", "[", extraInfo.c_str());
recover();
safeIndex = baseExpression->getType().getArraySize() - 1; safeIndex = baseExpression->getType().getArraySize() - 1;
} }
else if (baseExpression->getQualifier() == EvqFragData && index > 0 && else if (baseExpression->getQualifier() == EvqFragData && index > 0 &&
!isExtensionEnabled("GL_EXT_draw_buffers")) !isExtensionEnabled("GL_EXT_draw_buffers"))
{ {
error(location, "", "[", outOfRangeError(
"array indexes for gl_FragData must be zero when GL_EXT_draw_buffers is " outOfRangeIndexIsError, location, "", "[",
"disabled"); "array indexes for gl_FragData must be zero when GL_EXT_draw_buffers is "
recover(); "disabled");
safeIndex = 0; safeIndex = 0;
} }
} }
...@@ -2841,8 +2807,7 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, ...@@ -2841,8 +2807,7 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
std::stringstream extraInfoStream; std::stringstream extraInfoStream;
extraInfoStream << "field selection out of range '" << index << "'"; extraInfoStream << "field selection out of range '" << index << "'";
std::string extraInfo = extraInfoStream.str(); std::string extraInfo = extraInfoStream.str();
error(location, "", "[", extraInfo.c_str()); outOfRangeError(outOfRangeIndexIsError, location, "", "[", extraInfo.c_str());
recover();
safeIndex = baseExpression->getType().getNominalSize() - 1; safeIndex = baseExpression->getType().getNominalSize() - 1;
} }
...@@ -2945,32 +2910,29 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre ...@@ -2945,32 +2910,29 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre
recover(); recover();
} }
if (baseExpression->getType().getQualifier() == EvqConst && if (baseExpression->getAsConstantUnion())
baseExpression->getAsConstantUnion())
{ {
// constant folding for vector fields // constant folding for vector fields
indexedExpression = addConstVectorNode(fields, baseExpression, fieldLocation); indexedExpression = addConstVectorNode(fields, baseExpression->getAsConstantUnion(),
if (indexedExpression == 0) fieldLocation, true);
{
recover();
indexedExpression = baseExpression;
}
else
{
indexedExpression->setType(TType(baseExpression->getBasicType(),
baseExpression->getPrecision(), EvqConst,
(unsigned char)(fieldString).size()));
}
} }
else else
{ {
TString vectorString = fieldString;
TIntermTyped *index = intermediate.addSwizzle(fields, fieldLocation); TIntermTyped *index = intermediate.addSwizzle(fields, fieldLocation);
indexedExpression = indexedExpression =
intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation); intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation);
}
if (indexedExpression == nullptr)
{
recover();
indexedExpression = baseExpression;
}
else
{
// Note that the qualifier set here will be corrected later.
indexedExpression->setType(TType(baseExpression->getBasicType(), indexedExpression->setType(TType(baseExpression->getBasicType(),
baseExpression->getPrecision(), EvqTemporary, baseExpression->getPrecision(), EvqTemporary,
(unsigned char)vectorString.size())); (unsigned char)(fieldString).size()));
} }
} }
else if (baseExpression->getBasicType() == EbtStruct) else if (baseExpression->getBasicType() == EbtStruct)
...@@ -2996,8 +2958,7 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre ...@@ -2996,8 +2958,7 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre
} }
if (fieldFound) if (fieldFound)
{ {
if (baseExpression->getType().getQualifier() == EvqConst && if (baseExpression->getAsConstantUnion())
baseExpression->getAsConstantUnion())
{ {
indexedExpression = addConstStruct(fieldString, baseExpression, dotLocation); indexedExpression = addConstStruct(fieldString, baseExpression, dotLocation);
if (indexedExpression == 0) if (indexedExpression == 0)
...@@ -3008,9 +2969,6 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre ...@@ -3008,9 +2969,6 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre
else else
{ {
indexedExpression->setType(*fields[i]->type()); indexedExpression->setType(*fields[i]->type());
// change the qualifier of the return type, not of the structure field
// as the structure definition is shared between various structures.
indexedExpression->getTypePointer()->setQualifier(EvqConst);
} }
} }
else else
...@@ -3093,6 +3051,10 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre ...@@ -3093,6 +3051,10 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre
{ {
indexedExpression->getTypePointer()->setQualifier(EvqConst); indexedExpression->getTypePointer()->setQualifier(EvqConst);
} }
else
{
indexedExpression->getTypePointer()->setQualifier(EvqTemporary);
}
return indexedExpression; return indexedExpression;
} }
...@@ -3406,7 +3368,10 @@ TIntermCase *TParseContext::addCase(TIntermTyped *condition, const TSourceLoc &l ...@@ -3406,7 +3368,10 @@ TIntermCase *TParseContext::addCase(TIntermTyped *condition, const TSourceLoc &l
recover(); recover();
} }
TIntermConstantUnion *conditionConst = condition->getAsConstantUnion(); TIntermConstantUnion *conditionConst = condition->getAsConstantUnion();
if (conditionConst == nullptr) // TODO(oetuaho@nvidia.com): Get rid of the conditionConst == nullptr check once all constant
// expressions can be folded. Right now we don't allow constant expressions that ANGLE can't
// fold in case labels.
if (condition->getQualifier() != EvqConst || conditionConst == nullptr)
{ {
error(condition->getLine(), "case label must be constant", "case"); error(condition->getLine(), "case label must be constant", "case");
recover(); recover();
...@@ -3938,7 +3903,8 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, ...@@ -3938,7 +3903,8 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall,
// Some built-in functions have out parameters too. // Some built-in functions have out parameters too.
functionCallLValueErrorCheck(fnCandidate, aggregate); functionCallLValueErrorCheck(fnCandidate, aggregate);
// See if we can constant fold a built-in. // See if we can constant fold a built-in. Note that this may be possible even
// if it is not const-qualified.
TIntermTyped *foldedNode = intermediate.foldAggregateBuiltIn(aggregate); TIntermTyped *foldedNode = intermediate.foldAggregateBuiltIn(aggregate);
if (foldedNode) if (foldedNode)
{ {
......
...@@ -77,6 +77,13 @@ class TParseContext : angle::NonCopyable ...@@ -77,6 +77,13 @@ class TParseContext : angle::NonCopyable
void warning(const TSourceLoc &loc, const char *reason, const char *token, void warning(const TSourceLoc &loc, const char *reason, const char *token,
const char *extraInfo=""); const char *extraInfo="");
// If isError is false, a warning will be reported instead.
void outOfRangeError(bool isError,
const TSourceLoc &loc,
const char *reason,
const char *token,
const char *extraInfo = "");
void recover(); void recover();
TIntermNode *getTreeRoot() const { return mTreeRoot; } TIntermNode *getTreeRoot() const { return mTreeRoot; }
void setTreeRoot(TIntermNode *treeRoot) { mTreeRoot = treeRoot; } void setTreeRoot(TIntermNode *treeRoot) { mTreeRoot = treeRoot; }
...@@ -158,7 +165,7 @@ class TParseContext : angle::NonCopyable ...@@ -158,7 +165,7 @@ class TParseContext : angle::NonCopyable
void handlePragmaDirective(const TSourceLoc &loc, const char *name, const char *value, bool stdgl); void handlePragmaDirective(const TSourceLoc &loc, const char *name, const char *value, bool stdgl);
bool containsSampler(const TType &type); bool containsSampler(const TType &type);
bool areAllChildConst(TIntermAggregate *aggrNode); bool areAllChildrenConstantFolded(TIntermAggregate *aggrNode);
const TFunction* findFunction( const TFunction* findFunction(
const TSourceLoc &line, TFunction *pfnCall, int inputShaderVersion, bool *builtIn = 0); const TSourceLoc &line, TFunction *pfnCall, int inputShaderVersion, bool *builtIn = 0);
bool executeInitializer(const TSourceLoc &line, bool executeInitializer(const TSourceLoc &line,
...@@ -241,9 +248,18 @@ class TParseContext : angle::NonCopyable ...@@ -241,9 +248,18 @@ class TParseContext : angle::NonCopyable
TFunction *fnCall, TFunction *fnCall,
const TSourceLoc &line); const TSourceLoc &line);
TIntermTyped *foldConstConstructor(TIntermAggregate *aggrNode, const TType &type); TIntermTyped *foldConstConstructor(TIntermAggregate *aggrNode, const TType &type);
TIntermTyped *addConstVectorNode(TVectorFields&, TIntermTyped*, const TSourceLoc&); TIntermTyped *addConstVectorNode(TVectorFields &fields,
TIntermTyped *addConstMatrixNode(int, TIntermTyped*, const TSourceLoc&); TIntermConstantUnion *node,
TIntermTyped *addConstArrayNode(int index, TIntermTyped *node, const TSourceLoc &line); const TSourceLoc &line,
bool outOfRangeIndexIsError);
TIntermTyped *addConstMatrixNode(int index,
TIntermConstantUnion *node,
const TSourceLoc &line,
bool outOfRangeIndexIsError);
TIntermTyped *addConstArrayNode(int index,
TIntermConstantUnion *node,
const TSourceLoc &line,
bool outOfRangeIndexIsError);
TIntermTyped *addConstStruct( TIntermTyped *addConstStruct(
const TString &identifier, TIntermTyped *node, const TSourceLoc& line); const TString &identifier, TIntermTyped *node, const TSourceLoc& line);
TIntermTyped *addIndexExpression(TIntermTyped *baseExpression, TIntermTyped *addIndexExpression(TIntermTyped *baseExpression,
......
...@@ -1067,3 +1067,21 @@ TEST_F(MalformedShaderTest, TernaryOperatorAppliedToArrayConstructorIsConst) ...@@ -1067,3 +1067,21 @@ TEST_F(MalformedShaderTest, TernaryOperatorAppliedToArrayConstructorIsConst)
FAIL() << "Shader compilation failed, expecting success " << mInfoLog; FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
} }
} }
// Test that a ternary operator with one unevaluated non-constant operand is not a constant
// expression.
TEST_F(MalformedShaderTest, TernaryOperatorNonConstantOperand)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform float u;\n"
"void main()\n"
"{\n"
" const float f = true ? 1.0 : u;\n"
" gl_FragColor = vec4(f);\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
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