Commit ad6b8756 by Alexis Hetu Committed by Alexis Hétu

More ground work for Uniform blocks

Moved some of the struct / indexing code from glslang.y to the ParserHelper class and prepared it for uniform blocks. Change-Id: I2d5d380f662f36f04d74783fd542c4b258d3f3a5 Reviewed-on: https://swiftshader-review.googlesource.com/3441Tested-by: 's avatarAlexis Hétu <sugoi@google.com> Reviewed-by: 's avatarNicolas Capens <capn@google.com>
parent d71948f8
...@@ -981,6 +981,26 @@ bool TParseContext::extensionErrorCheck(int line, const TString& extension) ...@@ -981,6 +981,26 @@ bool TParseContext::extensionErrorCheck(int line, const TString& extension)
return false; return false;
} }
bool TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *aggregate)
{
for(size_t i = 0; i < fnCandidate->getParamCount(); ++i)
{
TQualifier qual = fnCandidate->getParam(i).type->getQualifier();
if(qual == EvqOut || qual == EvqInOut)
{
TIntermTyped *node = (aggregate->getSequence())[i]->getAsTyped();
if(lValueErrorCheck(node->getLine(), "assign", node))
{
error(node->getLine(),
"Constant value cannot be passed for 'out' or 'inout' parameters.", "Error");
recover();
return true;
}
}
}
return false;
}
bool TParseContext::supportsExtension(const char* extension) bool TParseContext::supportsExtension(const char* extension)
{ {
const TExtensionBehavior& extbehavior = extensionBehavior(); const TExtensionBehavior& extbehavior = extensionBehavior();
...@@ -1403,7 +1423,7 @@ TIntermTyped* TParseContext::addConstArrayNode(int index, TIntermTyped* node, TS ...@@ -1403,7 +1423,7 @@ TIntermTyped* TParseContext::addConstArrayNode(int index, TIntermTyped* node, TS
// If there is an embedded/nested struct, it appropriately calls addConstStructNested or addConstStructFromAggr // If there is an embedded/nested struct, it appropriately calls addConstStructNested or addConstStructFromAggr
// function and returns the parse-tree with the values of the embedded/nested struct. // function and returns the parse-tree with the values of the embedded/nested struct.
// //
TIntermTyped* TParseContext::addConstStruct(TString& identifier, TIntermTyped* node, TSourceLoc line) TIntermTyped* TParseContext::addConstStruct(const TString& identifier, TIntermTyped* node, TSourceLoc line)
{ {
const TFieldList &fields = node->getType().getStruct()->fields(); const TFieldList &fields = node->getType().getStruct()->fields();
TIntermTyped *typedNode; TIntermTyped *typedNode;
...@@ -1433,13 +1453,394 @@ TIntermTyped* TParseContext::addConstStruct(TString& identifier, TIntermTyped* n ...@@ -1433,13 +1453,394 @@ TIntermTyped* TParseContext::addConstStruct(TString& identifier, TIntermTyped* n
return typedNode; return typedNode;
} }
//
// Parse an array index expression
//
TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, const TSourceLoc &location, TIntermTyped *indexExpression)
{
TIntermTyped *indexedExpression = NULL;
if(!baseExpression->isArray() && !baseExpression->isMatrix() && !baseExpression->isVector())
{
if(baseExpression->getAsSymbolNode())
{
error(location, " left of '[' is not of type array, matrix, or vector ",
baseExpression->getAsSymbolNode()->getSymbol().c_str());
}
else
{
error(location, " left of '[' is not of type array, matrix, or vector ", "expression");
}
recover();
}
TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion();
if(indexExpression->getQualifier() == EvqConstExpr && indexConstantUnion)
{
int index = indexConstantUnion->getIConst(0);
if(index < 0)
{
std::stringstream infoStream;
infoStream << index;
std::string info = infoStream.str();
error(location, "negative index", info.c_str());
recover();
index = 0;
}
if(baseExpression->getType().getQualifier() == EvqConstExpr)
{
if(baseExpression->isArray())
{
// constant folding for arrays
indexedExpression = addConstArrayNode(index, baseExpression, location);
}
else if(baseExpression->isVector())
{
// constant folding for vectors
TVectorFields fields;
fields.num = 1;
fields.offsets[0] = index; // need to do it this way because v.xy sends fields integer array
indexedExpression = addConstVectorNode(fields, baseExpression, location);
}
else if(baseExpression->isMatrix())
{
// constant folding for matrices
indexedExpression = addConstMatrixNode(index, baseExpression, location);
}
}
else
{
int safeIndex = -1;
if(baseExpression->isArray())
{
if(index >= baseExpression->getType().getArraySize())
{
std::stringstream extraInfoStream;
extraInfoStream << "array index out of range '" << index << "'";
std::string extraInfo = extraInfoStream.str();
error(location, "", "[", extraInfo.c_str());
recover();
safeIndex = baseExpression->getType().getArraySize() - 1;
}
}
else if((baseExpression->isVector() || baseExpression->isMatrix()) &&
baseExpression->getType().getNominalSize() <= index)
{
std::stringstream extraInfoStream;
extraInfoStream << "field selection out of range '" << index << "'";
std::string extraInfo = extraInfoStream.str();
error(location, "", "[", extraInfo.c_str());
recover();
safeIndex = baseExpression->getType().getNominalSize() - 1;
}
// Don't modify the data of the previous constant union, because it can point
// to builtins, like gl_MaxDrawBuffers. Instead use a new sanitized object.
if(safeIndex != -1)
{
ConstantUnion *safeConstantUnion = new ConstantUnion();
safeConstantUnion->setIConst(safeIndex);
indexConstantUnion->replaceConstantUnion(safeConstantUnion);
}
indexedExpression = intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location);
}
}
else
{
if(baseExpression->isInterfaceBlock())
{
error(location, "",
"[", "array indexes for interface blocks arrays must be constant integral expressions");
recover();
}
// FIXME
/*
else if(baseExpression->getQualifier() == EvqFragmentOut)
{
error(location, "", "[", "array indexes for fragment outputs must be constant integral expressions");
recover();
}
*/
indexedExpression = intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location);
}
if(indexedExpression == 0)
{
ConstantUnion *unionArray = new ConstantUnion[1];
unionArray->setFConst(0.0f);
indexedExpression = intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConstExpr), location);
}
else if(baseExpression->isArray())
{
const TType &baseType = baseExpression->getType();
if(baseType.getStruct())
{
TType copyOfType(baseType.getStruct());
indexedExpression->setType(copyOfType);
}
else if(baseType.isInterfaceBlock())
{
TType copyOfType(baseType.getInterfaceBlock(), baseType.getQualifier(), baseType.getLayoutQualifier(), 0);
indexedExpression->setType(copyOfType);
}
else
{
indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(),
EvqTemporary, static_cast<unsigned char>(baseExpression->getNominalSize()),
static_cast<unsigned char>(baseExpression->getSecondarySize())));
}
if(baseExpression->getType().getQualifier() == EvqConstExpr)
{
indexedExpression->getTypePointer()->setQualifier(EvqConstExpr);
}
}
else if(baseExpression->isMatrix())
{
TQualifier qualifier = baseExpression->getType().getQualifier() == EvqConstExpr ? EvqConstExpr : EvqTemporary;
indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(),
qualifier, static_cast<unsigned char>(baseExpression->getSecondarySize())));
}
else if(baseExpression->isVector())
{
TQualifier qualifier = baseExpression->getType().getQualifier() == EvqConstExpr ? EvqConstExpr : EvqTemporary;
indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(), qualifier));
}
else
{
indexedExpression->setType(baseExpression->getType());
}
return indexedExpression;
}
TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpression, const TSourceLoc &dotLocation,
const TString &fieldString, const TSourceLoc &fieldLocation)
{
TIntermTyped *indexedExpression = NULL;
if(baseExpression->isArray())
{
error(fieldLocation, "cannot apply dot operator to an array", ".");
recover();
}
if(baseExpression->isVector())
{
TVectorFields fields;
if(!parseVectorFields(fieldString, baseExpression->getNominalSize(), fields, fieldLocation))
{
fields.num = 1;
fields.offsets[0] = 0;
recover();
}
if(baseExpression->getType().getQualifier() == EvqConstExpr)
{
// constant folding for vector fields
indexedExpression = addConstVectorNode(fields, baseExpression, fieldLocation);
if(indexedExpression == 0)
{
recover();
indexedExpression = baseExpression;
}
else
{
indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(),
EvqConstExpr, (unsigned char)(fieldString).size()));
}
}
else
{
TString vectorString = fieldString;
TIntermTyped *index = intermediate.addSwizzle(fields, fieldLocation);
indexedExpression = intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation);
indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(),
EvqTemporary, (unsigned char)vectorString.size()));
}
}
else if(baseExpression->isMatrix())
{
TMatrixFields fields;
if(!parseMatrixFields(fieldString, baseExpression->getNominalSize(), baseExpression->getSecondarySize(), fields, fieldLocation))
{
fields.wholeRow = false;
fields.wholeCol = false;
fields.row = 0;
fields.col = 0;
recover();
}
if(fields.wholeRow || fields.wholeCol)
{
error(dotLocation, " non-scalar fields not implemented yet", ".");
recover();
ConstantUnion *unionArray = new ConstantUnion[1];
unionArray->setIConst(0);
TIntermTyped *index = intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConstExpr),
fieldLocation);
indexedExpression = intermediate.addIndex(EOpIndexDirect, baseExpression, index, dotLocation);
indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(),
EvqTemporary, static_cast<unsigned char>(baseExpression->getNominalSize()),
static_cast<unsigned char>(baseExpression->getSecondarySize())));
}
else
{
ConstantUnion *unionArray = new ConstantUnion[1];
unionArray->setIConst(fields.col * baseExpression->getSecondarySize() + fields.row);
TIntermTyped *index = intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConstExpr),
fieldLocation);
indexedExpression = intermediate.addIndex(EOpIndexDirect, baseExpression, index, dotLocation);
indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision()));
}
}
else if(baseExpression->getBasicType() == EbtStruct)
{
bool fieldFound = false;
const TFieldList &fields = baseExpression->getType().getStruct()->fields();
if(fields.empty())
{
error(dotLocation, "structure has no fields", "Internal Error");
recover();
indexedExpression = baseExpression;
}
else
{
unsigned int i;
for(i = 0; i < fields.size(); ++i)
{
if(fields[i]->name() == fieldString)
{
fieldFound = true;
break;
}
}
if(fieldFound)
{
if(baseExpression->getType().getQualifier() == EvqConstExpr)
{
indexedExpression = addConstStruct(fieldString, baseExpression, dotLocation);
if(indexedExpression == 0)
{
recover();
indexedExpression = baseExpression;
}
else
{
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(EvqConstExpr);
}
}
else
{
ConstantUnion *unionArray = new ConstantUnion[1];
unionArray->setIConst(i);
TIntermTyped *index = intermediate.addConstantUnion(unionArray, *fields[i]->type(), fieldLocation);
indexedExpression = intermediate.addIndex(EOpIndexDirectStruct, baseExpression, index, dotLocation);
indexedExpression->setType(*fields[i]->type());
}
}
else
{
error(dotLocation, " no such field in structure", fieldString.c_str());
recover();
indexedExpression = baseExpression;
}
}
}
else if(baseExpression->isInterfaceBlock())
{
bool fieldFound = false;
const TFieldList &fields = baseExpression->getType().getInterfaceBlock()->fields();
if(fields.empty())
{
error(dotLocation, "interface block has no fields", "Internal Error");
recover();
indexedExpression = baseExpression;
}
else
{
unsigned int i;
for(i = 0; i < fields.size(); ++i)
{
if(fields[i]->name() == fieldString)
{
fieldFound = true;
break;
}
}
if(fieldFound)
{
ConstantUnion *unionArray = new ConstantUnion[1];
unionArray->setIConst(i);
TIntermTyped *index = intermediate.addConstantUnion(unionArray, *fields[i]->type(), fieldLocation);
indexedExpression = intermediate.addIndex(EOpIndexDirectInterfaceBlock, baseExpression, index,
dotLocation);
indexedExpression->setType(*fields[i]->type());
}
else
{
error(dotLocation, " no such field in interface block", fieldString.c_str());
recover();
indexedExpression = baseExpression;
}
}
}
else
{
if(shaderVersion < 300)
{
error(dotLocation, " field selection requires structure, vector, or matrix on left hand side",
fieldString.c_str());
}
else
{
error(dotLocation,
" field selection requires structure, vector, matrix, or interface block on left hand side",
fieldString.c_str());
}
recover();
indexedExpression = baseExpression;
}
return indexedExpression;
}
TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType, const TSourceLoc& qualifierTypeLine) TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType, const TSourceLoc& qualifierTypeLine)
{ {
TLayoutQualifier qualifier; TLayoutQualifier qualifier;
qualifier.location = -1; qualifier.location = -1;
qualifier.matrixPacking = EmpUnspecified;
qualifier.blockStorage = EbsUnspecified;
if (qualifierType == "location") if(qualifierType == "shared")
{
qualifier.blockStorage = EbsShared;
}
else if(qualifierType == "packed")
{
qualifier.blockStorage = EbsPacked;
}
else if(qualifierType == "std140")
{
qualifier.blockStorage = EbsStd140;
}
else if(qualifierType == "row_major")
{
qualifier.matrixPacking = EmpRowMajor;
}
else if(qualifierType == "column_major")
{
qualifier.matrixPacking = EmpColumnMajor;
}
else if(qualifierType == "location")
{ {
error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(), "location requires an argument"); error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(), "location requires an argument");
recover(); recover();
...@@ -1458,6 +1859,8 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierTyp ...@@ -1458,6 +1859,8 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierTyp
TLayoutQualifier qualifier; TLayoutQualifier qualifier;
qualifier.location = -1; qualifier.location = -1;
qualifier.matrixPacking = EmpUnspecified;
qualifier.blockStorage = EbsUnspecified;
if (qualifierType != "location") if (qualifierType != "location")
{ {
...@@ -1489,6 +1892,14 @@ TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualif ...@@ -1489,6 +1892,14 @@ TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualif
{ {
joinedQualifier.location = rightQualifier.location; joinedQualifier.location = rightQualifier.location;
} }
if(rightQualifier.matrixPacking != EmpUnspecified)
{
joinedQualifier.matrixPacking = rightQualifier.matrixPacking;
}
if(rightQualifier.blockStorage != EbsUnspecified)
{
joinedQualifier.blockStorage = rightQualifier.blockStorage;
}
return joinedQualifier; return joinedQualifier;
} }
...@@ -1539,6 +1950,98 @@ TPublicType TParseContext::joinInterpolationQualifiers(const TSourceLoc &interpo ...@@ -1539,6 +1950,98 @@ TPublicType TParseContext::joinInterpolationQualifiers(const TSourceLoc &interpo
return type; return type;
} }
TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *fieldList)
{
if(voidErrorCheck(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier))
{
recover();
}
for(unsigned int i = 0; i < fieldList->size(); ++i)
{
//
// Careful not to replace already known aspects of type, like array-ness
//
TType *type = (*fieldList)[i]->type();
type->setBasicType(typeSpecifier.type);
type->setNominalSize(typeSpecifier.primarySize);
type->setSecondarySize(typeSpecifier.secondarySize);
type->setPrecision(typeSpecifier.precision);
type->setQualifier(typeSpecifier.qualifier);
type->setLayoutQualifier(typeSpecifier.layoutQualifier);
// don't allow arrays of arrays
if(type->isArray())
{
if(arrayTypeErrorCheck(typeSpecifier.line, typeSpecifier))
recover();
}
if(typeSpecifier.array)
type->setArraySize(typeSpecifier.arraySize);
if(typeSpecifier.userDef)
{
type->setStruct(typeSpecifier.userDef->getStruct());
}
if(structNestingErrorCheck(typeSpecifier.line, *(*fieldList)[i]))
{
recover();
}
}
return fieldList;
}
TPublicType TParseContext::addStructure(const TSourceLoc &structLine, const TSourceLoc &nameLine,
const TString *structName, TFieldList *fieldList)
{
TStructure *structure = new TStructure(structName, fieldList);
TType *structureType = new TType(structure);
// Store a bool in the struct if we're at global scope, to allow us to
// skip the local struct scoping workaround in HLSL.
structure->setUniqueId(TSymbolTableLevel::nextUniqueId());
structure->setAtGlobalScope(symbolTable.atGlobalLevel());
if(!structName->empty())
{
if(reservedErrorCheck(nameLine, *structName))
{
recover();
}
TVariable *userTypeDef = new TVariable(structName, *structureType, true);
if(!symbolTable.declare(*userTypeDef))
{
error(nameLine, "redefinition", structName->c_str(), "struct");
recover();
}
}
// ensure we do not specify any storage qualifiers on the struct members
for(unsigned int typeListIndex = 0; typeListIndex < fieldList->size(); typeListIndex++)
{
const TField &field = *(*fieldList)[typeListIndex];
const TQualifier qualifier = field.type()->getQualifier();
switch(qualifier)
{
case EvqGlobal:
case EvqTemporary:
break;
default:
error(field.line(), "invalid qualifier on struct member", getQualifierString(qualifier));
recover();
break;
}
}
TPublicType publicType;
publicType.setBasic(EbtStruct, EvqTemporary, structLine);
publicType.userDef = structureType;
exitStructDeclaration();
return publicType;
}
bool TParseContext::enterStructDeclaration(int line, const TString& identifier) bool TParseContext::enterStructDeclaration(int line, const TString& identifier)
{ {
++structNestingLevel; ++structNestingLevel;
...@@ -1559,6 +2062,210 @@ void TParseContext::exitStructDeclaration() ...@@ -1559,6 +2062,210 @@ void TParseContext::exitStructDeclaration()
--structNestingLevel; --structNestingLevel;
} }
bool TParseContext::structNestingErrorCheck(const TSourceLoc &line, const TField &field)
{
static const int kWebGLMaxStructNesting = 4;
if(field.type()->getBasicType() != EbtStruct)
{
return false;
}
// We're already inside a structure definition at this point, so add
// one to the field's struct nesting.
if(1 + field.type()->getDeepestStructNesting() > kWebGLMaxStructNesting)
{
std::stringstream reasonStream;
reasonStream << "Reference of struct type "
<< field.type()->getStruct()->name().c_str()
<< " exceeds maximum allowed nesting level of "
<< kWebGLMaxStructNesting;
std::string reason = reasonStream.str();
error(line, reason.c_str(), field.name().c_str(), "");
return true;
}
return false;
}
TIntermTyped *TParseContext::createUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc, const TType *funcReturnType)
{
if(child == nullptr)
{
return nullptr;
}
switch(op)
{
case EOpLogicalNot:
if(child->getBasicType() != EbtBool ||
child->isMatrix() ||
child->isArray() ||
child->isVector())
{
return nullptr;
}
break;
case EOpBitwiseNot:
if((child->getBasicType() != EbtInt && child->getBasicType() != EbtUInt) ||
child->isMatrix() ||
child->isArray())
{
return nullptr;
}
break;
case EOpPostIncrement:
case EOpPreIncrement:
case EOpPostDecrement:
case EOpPreDecrement:
case EOpNegative:
if(child->getBasicType() == EbtStruct ||
child->getBasicType() == EbtBool ||
child->isArray())
{
return nullptr;
}
// Operators for built-ins are already type checked against their prototype.
default:
break;
}
return intermediate.addUnaryMath(op, child, loc); // FIXME , funcReturnType);
}
TIntermTyped *TParseContext::addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc)
{
TIntermTyped *node = createUnaryMath(op, child, loc, nullptr);
if(node == nullptr)
{
unaryOpError(loc, getOperatorString(op), child->getCompleteString());
recover();
return child;
}
return node;
}
TIntermTyped *TParseContext::addUnaryMathLValue(TOperator op, TIntermTyped *child, const TSourceLoc &loc)
{
if(lValueErrorCheck(loc, getOperatorString(op), child))
recover();
return addUnaryMath(op, child, loc);
}
bool TParseContext::binaryOpCommonCheck(TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc)
{
if(left->isArray() || right->isArray())
{
if(shaderVersion < 300)
{
error(loc, "Invalid operation for arrays", getOperatorString(op));
return false;
}
if(left->isArray() != right->isArray())
{
error(loc, "array / non-array mismatch", getOperatorString(op));
return false;
}
switch(op)
{
case EOpEqual:
case EOpNotEqual:
case EOpAssign:
case EOpInitialize:
break;
default:
error(loc, "Invalid operation for arrays", getOperatorString(op));
return false;
}
// At this point, size of implicitly sized arrays should be resolved.
if(left->getArraySize() != right->getArraySize())
{
error(loc, "array size mismatch", getOperatorString(op));
return false;
}
}
// Check ops which require integer / ivec parameters
bool isBitShift = false;
switch(op)
{
case EOpBitShiftLeft:
case EOpBitShiftRight:
case EOpBitShiftLeftAssign:
case EOpBitShiftRightAssign:
// Unsigned can be bit-shifted by signed and vice versa, but we need to
// check that the basic type is an integer type.
isBitShift = true;
if(!IsInteger(left->getBasicType()) || !IsInteger(right->getBasicType()))
{
return false;
}
break;
case EOpBitwiseAnd:
case EOpBitwiseXor:
case EOpBitwiseOr:
case EOpBitwiseAndAssign:
case EOpBitwiseXorAssign:
case EOpBitwiseOrAssign:
// It is enough to check the type of only one operand, since later it
// is checked that the operand types match.
if(!IsInteger(left->getBasicType()))
{
return false;
}
break;
default:
break;
}
// GLSL ES 1.00 and 3.00 do not support implicit type casting.
// So the basic type should usually match.
if(!isBitShift && left->getBasicType() != right->getBasicType())
{
return false;
}
// Check that type sizes match exactly on ops that require that.
// Also check restrictions for structs that contain arrays or samplers.
switch(op)
{
case EOpAssign:
case EOpInitialize:
case EOpEqual:
case EOpNotEqual:
// ESSL 1.00 sections 5.7, 5.8, 5.9
if(shaderVersion < 300 && left->getType().isStructureContainingArrays())
{
error(loc, "undefined operation for structs containing arrays", getOperatorString(op));
return false;
}
// Samplers as l-values are disallowed also in ESSL 3.00, see section 4.1.7,
// we interpret the spec so that this extends to structs containing samplers,
// similarly to ESSL 1.00 spec.
if((shaderVersion < 300 || op == EOpAssign || op == EOpInitialize) &&
left->getType().isStructureContainingSamplers())
{
error(loc, "undefined operation for structs containing samplers", getOperatorString(op));
return false;
}
case EOpLessThan:
case EOpGreaterThan:
case EOpLessThanEqual:
case EOpGreaterThanEqual:
if((left->getNominalSize() != right->getNominalSize()) ||
(left->getSecondarySize() != right->getSecondarySize()))
{
return false;
}
default:
break;
}
return true;
}
// //
// Parse an array of strings using yyparse. // Parse an array of strings using yyparse.
// //
......
...@@ -39,6 +39,8 @@ struct TParseContext { ...@@ -39,6 +39,8 @@ struct TParseContext {
currentFunctionType(NULL), currentFunctionType(NULL),
functionReturnsValue(false), functionReturnsValue(false),
checksPrecisionErrors(checksPrecErrors), checksPrecisionErrors(checksPrecErrors),
defaultMatrixPacking(EmpColumnMajor),
defaultBlockStorage(EbsShared),
diagnostics(is), diagnostics(is),
shaderVersion(100), shaderVersion(100),
directiveHandler(ext, diagnostics, shaderVersion), directiveHandler(ext, diagnostics, shaderVersion),
...@@ -58,6 +60,8 @@ struct TParseContext { ...@@ -58,6 +60,8 @@ struct TParseContext {
const TType* currentFunctionType; // the return type of the function that's currently being parsed const TType* currentFunctionType; // the return type of the function that's currently being parsed
bool functionReturnsValue; // true if a non-void function has a return bool functionReturnsValue; // true if a non-void function has a return
bool checksPrecisionErrors; // true if an error will be generated when a variable is declared without precision, explicit or implicit. bool checksPrecisionErrors; // true if an error will be generated when a variable is declared without precision, explicit or implicit.
TLayoutMatrixPacking defaultMatrixPacking;
TLayoutBlockStorage defaultBlockStorage;
TString HashErrMsg; TString HashErrMsg;
bool AfterEOF; bool AfterEOF;
TDiagnostics diagnostics; TDiagnostics diagnostics;
...@@ -102,7 +106,8 @@ struct TParseContext { ...@@ -102,7 +106,8 @@ struct TParseContext {
bool nonInitErrorCheck(int line, TString& identifier, TPublicType& type, TVariable*& variable); bool nonInitErrorCheck(int line, TString& identifier, TPublicType& type, TVariable*& variable);
bool paramErrorCheck(int line, TQualifier qualifier, TQualifier paramQualifier, TType* type); bool paramErrorCheck(int line, TQualifier qualifier, TQualifier paramQualifier, TType* type);
bool extensionErrorCheck(int line, const TString&); bool extensionErrorCheck(int line, const TString&);
bool layoutLocationErrorCheck(const TSourceLoc &location, const TLayoutQualifier &layoutQualifier); bool layoutLocationErrorCheck(const TSourceLoc& location, const TLayoutQualifier &layoutQualifier);
bool functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *);
const TExtensionBehavior& extensionBehavior() const { return directiveHandler.extensionBehavior(); } const TExtensionBehavior& extensionBehavior() const { return directiveHandler.extensionBehavior(); }
bool supportsExtension(const char* extension); bool supportsExtension(const char* extension);
...@@ -125,7 +130,12 @@ struct TParseContext { ...@@ -125,7 +130,12 @@ struct TParseContext {
TIntermTyped* addConstVectorNode(TVectorFields&, TIntermTyped*, TSourceLoc); TIntermTyped* addConstVectorNode(TVectorFields&, TIntermTyped*, TSourceLoc);
TIntermTyped* addConstMatrixNode(int , TIntermTyped*, TSourceLoc); TIntermTyped* addConstMatrixNode(int , TIntermTyped*, TSourceLoc);
TIntermTyped* addConstArrayNode(int index, TIntermTyped* node, TSourceLoc line); TIntermTyped* addConstArrayNode(int index, TIntermTyped* node, TSourceLoc line);
TIntermTyped* addConstStruct(TString& , TIntermTyped*, TSourceLoc); TIntermTyped* addConstStruct(const TString& , TIntermTyped*, TSourceLoc);
TIntermTyped *addIndexExpression(TIntermTyped *baseExpression, const TSourceLoc& location, TIntermTyped *indexExpression);
TIntermTyped* addFieldSelectionExpression(TIntermTyped *baseExpression, const TSourceLoc &dotLocation, const TString &fieldString, const TSourceLoc &fieldLocation);
TFieldList *addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *fieldList);
TPublicType addStructure(const TSourceLoc &structLine, const TSourceLoc &nameLine, const TString *structName, TFieldList *fieldList);
TLayoutQualifier parseLayoutQualifier(const TString &qualifierType, const TSourceLoc& qualifierTypeLine); TLayoutQualifier parseLayoutQualifier(const TString &qualifierType, const TSourceLoc& qualifierTypeLine);
TLayoutQualifier parseLayoutQualifier(const TString &qualifierType, const TSourceLoc& qualifierTypeLine, const TString &intValueString, int intValue, const TSourceLoc& intValueLine); TLayoutQualifier parseLayoutQualifier(const TString &qualifierType, const TSourceLoc& qualifierTypeLine, const TString &intValueString, int intValue, const TSourceLoc& intValueLine);
...@@ -138,7 +148,18 @@ struct TParseContext { ...@@ -138,7 +148,18 @@ struct TParseContext {
bool enterStructDeclaration(TSourceLoc line, const TString& identifier); bool enterStructDeclaration(TSourceLoc line, const TString& identifier);
void exitStructDeclaration(); void exitStructDeclaration();
bool structNestingErrorCheck(TSourceLoc line, const TType& fieldType); bool structNestingErrorCheck(const TSourceLoc &line, const TField &field);
TIntermTyped *addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc);
TIntermTyped *addUnaryMathLValue(TOperator op, TIntermTyped *child, const TSourceLoc &loc);
private:
// The funcReturnType parameter is expected to be non-null when the operation is a built-in function.
// It is expected to be null for other unary operators.
TIntermTyped *createUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc, const TType *funcReturnType);
// Return true if the checks pass
bool binaryOpCommonCheck(TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
}; };
int PaParseStrings(int count, const char* const string[], const int length[], int PaParseStrings(int count, const char* const string[], const int length[],
......
...@@ -193,7 +193,7 @@ public: ...@@ -193,7 +193,7 @@ public:
bool insert(TSymbol &symbol) bool insert(TSymbol &symbol)
{ {
symbol.setUniqueId(++uniqueId); symbol.setUniqueId(nextUniqueId());
// //
// returning true means symbol was added to the table // returning true means symbol was added to the table
...@@ -213,6 +213,11 @@ public: ...@@ -213,6 +213,11 @@ public:
return (*it).second; return (*it).second;
} }
static int nextUniqueId()
{
return ++uniqueId;
}
protected: protected:
tLevel level; tLevel level;
static int uniqueId; // for unique identification in code generation static int uniqueId; // for unique identification in code generation
......
...@@ -255,208 +255,19 @@ postfix_expression ...@@ -255,208 +255,19 @@ postfix_expression
$$ = $1; $$ = $1;
} }
| postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET { | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET {
if (!$1->isArray() && !$1->isMatrix() && !$1->isVector()) { $$ = context->addIndexExpression($1, $2.line, $3);
if ($1->getAsSymbolNode())
context->error($2.line, " left of '[' is not of type array, matrix, or vector ", $1->getAsSymbolNode()->getSymbol().c_str());
else
context->error($2.line, " left of '[' is not of type array, matrix, or vector ", "expression");
context->recover();
}
if ($1->getType().getQualifier() == EvqConstExpr && $3->getQualifier() == EvqConstExpr) {
if ($1->isArray()) { // constant folding for arrays
$$ = context->addConstArrayNode($3->getAsConstantUnion()->getIConst(0), $1, $2.line);
} else if ($1->isVector()) { // constant folding for vectors
TVectorFields fields;
fields.num = 1;
fields.offsets[0] = $3->getAsConstantUnion()->getIConst(0); // need to do it this way because v.xy sends fields integer array
$$ = context->addConstVectorNode(fields, $1, $2.line);
} else if ($1->isMatrix()) { // constant folding for matrices
$$ = context->addConstMatrixNode($3->getAsConstantUnion()->getIConst(0), $1, $2.line);
}
} else {
if ($3->getQualifier() == EvqConstExpr) {
if (($1->isVector() || $1->isMatrix()) && $1->getType().getNominalSize() <= $3->getAsConstantUnion()->getIConst(0) && !$1->isArray() ) {
std::stringstream extraInfoStream;
extraInfoStream << "field selection out of range '" << $3->getAsConstantUnion()->getIConst(0) << "'";
std::string extraInfo = extraInfoStream.str();
context->error($2.line, "", "[", extraInfo.c_str());
context->recover();
} else {
if ($1->isArray()) {
if ($1->getType().getArraySize() == 0) {
if ($1->getType().getMaxArraySize() <= $3->getAsConstantUnion()->getIConst(0)) {
if (context->arraySetMaxSize($1->getAsSymbolNode(), $1->getTypePointer(), $3->getAsConstantUnion()->getIConst(0), true, $2.line))
context->recover();
} else {
if (context->arraySetMaxSize($1->getAsSymbolNode(), $1->getTypePointer(), 0, false, $2.line))
context->recover();
}
} else if ( $3->getAsConstantUnion()->getIConst(0) >= $1->getType().getArraySize()) {
std::stringstream extraInfoStream;
extraInfoStream << "array index out of range '" << $3->getAsConstantUnion()->getIConst(0) << "'";
std::string extraInfo = extraInfoStream.str();
context->error($2.line, "", "[", extraInfo.c_str());
context->recover();
}
}
$$ = context->intermediate.addIndex(EOpIndexDirect, $1, $3, $2.line);
}
} else {
if ($1->isArray() && $1->getType().getArraySize() == 0) {
context->error($2.line, "", "[", "array must be redeclared with a size before being indexed with a variable");
context->recover();
}
$$ = context->intermediate.addIndex(EOpIndexIndirect, $1, $3, $2.line);
}
}
if ($$ == 0) {
ConstantUnion *unionArray = new ConstantUnion[1];
unionArray->setFConst(0.0f);
$$ = context->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConstExpr), $2.line);
} else if ($1->isArray()) {
if ($1->getType().getStruct())
$$->setType(TType($1->getType().getStruct()));
else
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary, $1->getNominalSize(), $1->getSecondarySize()));
if ($1->getType().getQualifier() == EvqConstExpr)
$$->getTypePointer()->setQualifier(EvqConstExpr);
} else if ($1->isMatrix() && $1->getType().getQualifier() == EvqConstExpr)
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqConstExpr, $1->getSecondarySize()));
else if ($1->isMatrix())
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary, $1->getSecondarySize()));
else if ($1->isVector() && $1->getType().getQualifier() == EvqConstExpr)
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqConstExpr));
else if ($1->isVector())
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary));
else
$$->setType($1->getType());
} }
| function_call { | function_call {
$$ = $1; $$ = $1;
} }
| postfix_expression DOT FIELD_SELECTION { | postfix_expression DOT FIELD_SELECTION {
if ($1->isArray()) { $$ = context->addFieldSelectionExpression($1, $2.line, *$3.string, $3.line);
context->error($3.line, "cannot apply dot operator to an array", ".");
context->recover();
}
if ($1->isVector()) {
TVectorFields fields;
if (! context->parseVectorFields(*$3.string, $1->getNominalSize(), fields, $3.line)) {
fields.num = 1;
fields.offsets[0] = 0;
context->recover();
}
if ($1->getType().getQualifier() == EvqConstExpr) { // constant folding for vector fields
$$ = context->addConstVectorNode(fields, $1, $3.line);
if ($$ == 0) {
context->recover();
$$ = $1;
}
else
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqConstExpr, (int) (*$3.string).size()));
} else {
TString vectorString = *$3.string;
TIntermTyped* index = context->intermediate.addSwizzle(fields, $3.line);
$$ = context->intermediate.addIndex(EOpVectorSwizzle, $1, index, $2.line);
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary, (int) vectorString.size()));
}
} else if ($1->isMatrix()) {
TMatrixFields fields;
if (! context->parseMatrixFields(*$3.string, $1->getNominalSize(), $1->getSecondarySize(), fields, $3.line)) {
fields.wholeRow = false;
fields.wholeCol = false;
fields.row = 0;
fields.col = 0;
context->recover();
}
if (fields.wholeRow || fields.wholeCol) {
context->error($2.line, " non-scalar fields not implemented yet", ".");
context->recover();
ConstantUnion *unionArray = new ConstantUnion[1];
unionArray->setIConst(0);
TIntermTyped* index = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConstExpr), $3.line);
$$ = context->intermediate.addIndex(EOpIndexDirect, $1, index, $2.line);
$$->setType(TType($1->getBasicType(), $1->getPrecision(),EvqTemporary, $1->getNominalSize()));
} else {
ConstantUnion *unionArray = new ConstantUnion[1];
unionArray->setIConst(fields.col * $1->getNominalSize() + fields.row);
TIntermTyped* index = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConstExpr), $3.line);
$$ = context->intermediate.addIndex(EOpIndexDirect, $1, index, $2.line);
$$->setType(TType($1->getBasicType(), $1->getPrecision()));
}
} else if ($1->getBasicType() == EbtStruct) {
bool fieldFound = false;
const TStructure* structure = $1->getType().getStruct();
if (structure == 0) {
context->error($2.line, "structure has no fields", "Internal Error");
context->recover();
$$ = $1;
} else {
unsigned int i;
const TFieldList& fields = structure->fields();
for (i = 0; i < fields.size(); ++i) {
if (fields[i]->name() == *$3.string) {
fieldFound = true;
break;
}
}
if (fieldFound) {
if ($1->getType().getQualifier() == EvqConstExpr) {
$$ = context->addConstStruct(*$3.string, $1, $2.line);
if ($$ == 0) {
context->recover();
$$ = $1;
}
else {
$$->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.
$$->getTypePointer()->setQualifier(EvqConstExpr);
}
} else {
ConstantUnion *unionArray = new ConstantUnion[1];
unionArray->setIConst(i);
TIntermTyped* index = context->intermediate.addConstantUnion(unionArray, *(fields[i]->type()), $3.line);
$$ = context->intermediate.addIndex(EOpIndexDirectStruct, $1, index, $2.line);
$$->setType(*fields[i]->type());
}
} else {
context->error($2.line, " no such field in structure", $3.string->c_str());
context->recover();
$$ = $1;
}
}
} else {
context->error($2.line, " field selection requires structure, vector, or matrix on left hand side", $3.string->c_str());
context->recover();
$$ = $1;
}
// don't delete $3.string, it's from the pool
} }
| postfix_expression INC_OP { | postfix_expression INC_OP {
if (context->lValueErrorCheck($2.line, "++", $1)) $$ = context->addUnaryMathLValue(EOpPostIncrement, $1, $2.line);
context->recover();
$$ = context->intermediate.addUnaryMath(EOpPostIncrement, $1, $2.line);
if ($$ == 0) {
context->unaryOpError($2.line, "++", $1->getCompleteString());
context->recover();
$$ = $1;
}
} }
| postfix_expression DEC_OP { | postfix_expression DEC_OP {
if (context->lValueErrorCheck($2.line, "--", $1)) $$ = context->addUnaryMathLValue(EOpPostDecrement, $1, $2.line);
context->recover();
$$ = context->intermediate.addUnaryMath(EOpPostDecrement, $1, $2.line);
if ($$ == 0) {
context->unaryOpError($2.line, "--", $1->getCompleteString());
context->recover();
$$ = $1;
}
} }
; ;
...@@ -2005,25 +1816,10 @@ type_specifier_nonarray ...@@ -2005,25 +1816,10 @@ type_specifier_nonarray
struct_specifier struct_specifier
: STRUCT IDENTIFIER LEFT_BRACE { if (context->enterStructDeclaration($2.line, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE { : STRUCT IDENTIFIER LEFT_BRACE { if (context->enterStructDeclaration($2.line, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE {
if (context->reservedErrorCheck($2.line, *$2.string)) $$ = context->addStructure($1.line, $2.line, $2.string, $5);
context->recover();
TType* structure = new TType(new TStructure($2.string, $5));
TVariable* userTypeDef = new TVariable($2.string, *structure, true);
if (! context->symbolTable.declare(*userTypeDef)) {
context->error($2.line, "redefinition", $2.string->c_str(), "struct");
context->recover();
}
$$.setBasic(EbtStruct, EvqTemporary, $1.line);
$$.userDef = structure;
context->exitStructDeclaration();
} }
| STRUCT LEFT_BRACE { if (context->enterStructDeclaration($2.line, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE { | STRUCT LEFT_BRACE { if (context->enterStructDeclaration($2.line, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE {
TString emptyName(""); $$ = context->addStructure($1.line, $1.line, NewPoolTString(""), $4);
TType* structure = new TType(new TStructure(&emptyName, $4));
$$.setBasic(EbtStruct, EvqTemporary, $1.line);
$$.userDef = structure;
context->exitStructDeclaration();
} }
; ;
...@@ -2048,32 +1844,13 @@ struct_declaration_list ...@@ -2048,32 +1844,13 @@ struct_declaration_list
struct_declaration struct_declaration
: type_specifier struct_declarator_list SEMICOLON { : type_specifier struct_declarator_list SEMICOLON {
$$ = $2; $$ = context->addStructDeclaratorList($1, $2);
}
if (context->voidErrorCheck($1.line, (*$2)[0]->name(), $1)) { | type_qualifier type_specifier struct_declarator_list SEMICOLON {
context->recover(); // ES3 Only, but errors should be handled elsewhere
} $2.qualifier = $1.qualifier;
for (unsigned int i = 0; i < $$->size(); ++i) { $2.layoutQualifier = $1.layoutQualifier;
// $$ = context->addStructDeclaratorList($2, $3);
// Careful not to replace already known aspects of type, like array-ness
//
TType* type = (*$$)[i]->type();
type->setBasicType($1.type);
type->setNominalSize($1.primarySize);
type->setSecondarySize($1.secondarySize);
type->setPrecision($1.precision);
// don't allow arrays of arrays
if (type->isArray()) {
if (context->arrayTypeErrorCheck($1.line, $1))
context->recover();
}
if ($1.array)
type->setArraySize($1.arraySize);
if ($1.userDef) {
type->setStruct($1.userDef->getStruct());
}
}
} }
; ;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -89,6 +89,7 @@ enum TOperator { ...@@ -89,6 +89,7 @@ enum TOperator {
EOpIndexDirect, EOpIndexDirect,
EOpIndexIndirect, EOpIndexIndirect,
EOpIndexDirectStruct, EOpIndexDirectStruct,
EOpIndexDirectInterfaceBlock,
EOpVectorSwizzle, EOpVectorSwizzle,
...@@ -295,6 +296,7 @@ public: ...@@ -295,6 +296,7 @@ public:
int getNominalSize() const { return type.getNominalSize(); } int getNominalSize() const { return type.getNominalSize(); }
int getSecondarySize() const { return type.getSecondarySize(); } int getSecondarySize() const { return type.getSecondarySize(); }
bool isInterfaceBlock() const { return type.isInterfaceBlock(); }
bool isMatrix() const { return type.isMatrix(); } bool isMatrix() const { return type.isMatrix(); }
bool isArray() const { return type.isArray(); } bool isArray() const { return type.isArray(); }
bool isVector() const { return type.isVector(); } bool isVector() const { return type.isVector(); }
...@@ -412,6 +414,9 @@ public: ...@@ -412,6 +414,9 @@ public:
float getFConst(int index) const { return unionArrayPointer ? unionArrayPointer[index].getFConst() : 0.0f; } float getFConst(int index) const { return unionArrayPointer ? unionArrayPointer[index].getFConst() : 0.0f; }
bool getBConst(int index) const { return unionArrayPointer ? unionArrayPointer[index].getBConst() : false; } bool getBConst(int index) const { return unionArrayPointer ? unionArrayPointer[index].getBConst() : false; }
// Previous union pointer freed on pool deallocation.
void replaceConstantUnion(ConstantUnion *safeConstantUnion) { unionArrayPointer = safeConstantUnion; }
virtual TIntermConstantUnion* getAsConstantUnion() { return this; } virtual TIntermConstantUnion* getAsConstantUnion() { return this; }
virtual void traverse(TIntermTraverser*); virtual void traverse(TIntermTraverser*);
......
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