blob: e89684bf7c64da1bccf3d655a26877effd89ef19 [file] [log] [blame] [edit]
//
// Copyright 2002 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef COMPILER_TRANSLATOR_PARSECONTEXT_H_
#define COMPILER_TRANSLATOR_PARSECONTEXT_H_
#include "common/hash_containers.h"
#include "common/span.h"
#include "compiler/preprocessor/Preprocessor.h"
#include "compiler/translator/Compiler.h"
#include "compiler/translator/Declarator.h"
#include "compiler/translator/Diagnostics.h"
#include "compiler/translator/DirectiveHandler.h"
#include "compiler/translator/FunctionLookup.h"
#include "compiler/translator/QualifierTypes.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/ValidateVaryingLocations.h"
namespace sh
{
struct TMatrixFields
{
bool wholeRow;
bool wholeCol;
int row;
int col;
};
struct ClipCullDistanceInfo
{
// Whether the size is specified by redeclaring the built-in
uint32_t size = 0;
// What is the maximum constant index used with this built-in
int32_t maxIndex = -1;
// Whether any non-constant indices were used with this built-in
bool hasNonConstIndex = false;
// Whether .length() has been called on this built-in
bool hasArrayLengthMethodCall = false;
// A location to associate with post-parse errors
TSourceLoc firstEncounter = kNoSourceLoc;
// The IR id of this variable, only needed when !declared
ir::VariableId id = ir::kInvalidVariableId;
};
enum class GeomTessArray
{
Sized,
Deferred,
};
enum class FunctionDeclaration
{
Prototype,
Definition,
};
struct VariableAndLocation
{
TSourceLoc line = {};
const TVariable *variable = nullptr;
};
//
// The following are extra variables needed during parsing, grouped together so
// they can be passed to the parser without needing a global.
//
class TParseContext : angle::NonCopyable
{
public:
TParseContext(TSymbolTable &symt,
TExtensionBehavior &ext,
sh::GLenum type,
ShShaderSpec spec,
const ShCompileOptions &options,
TDiagnostics *diagnostics,
const ShBuiltInResources &resources,
ShShaderOutput outputType);
~TParseContext();
bool anyMultiviewExtensionAvailable();
const angle::pp::Preprocessor &getPreprocessor() const { return mPreprocessor; }
angle::pp::Preprocessor &getPreprocessor() { return mPreprocessor; }
void *getScanner() const { return mScanner; }
void setScanner(void *scanner) { mScanner = scanner; }
int getShaderVersion() const { return mShaderVersion; }
void onShaderVersionDeclared(int version);
sh::GLenum getShaderType() const { return mShaderType; }
ShShaderSpec getShaderSpec() const { return mShaderSpec; }
int numErrors() const { return mDiagnostics->numErrors(); }
void error(const TSourceLoc &loc, const char *reason, const char *token);
void error(const TSourceLoc &loc, const char *reason, const ImmutableString &token);
void warning(const TSourceLoc &loc, const char *reason, const char *token);
// If isError is false, a warning will be reported instead.
void outOfRangeError(bool isError,
const TSourceLoc &loc,
const char *reason,
const char *token);
TIntermBlock *getTreeRoot() const { return mTreeRoot; }
void setTreeRoot(TIntermBlock *treeRoot);
ir::IR getIR();
bool usesDerivatives() const { return mUsesDerivatives; }
bool isEarlyFragmentTestsSpecified() const { return mEarlyFragmentTestsSpecified; }
bool hasDiscard() const { return mHasDiscard; }
bool isSampleQualifierSpecified() const { return mSampleQualifierSpecified; }
bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
sh::WorkGroupSize getComputeShaderLocalSize() const;
int getNumViews() const { return mNumViews; }
const std::map<int, ShPixelLocalStorageLayout> &pixelLocalStorageLayouts() const
{
return mPLSLayouts;
}
void enterFunctionDeclaration() { mDeclaringFunction = true; }
void exitFunctionDeclaration() { mDeclaringFunction = false; }
bool declaringFunction() const { return mDeclaringFunction; }
TIntermConstantUnion *addScalarLiteral(const TConstantUnion *constantUnion,
const TSourceLoc &line);
// This method is guaranteed to succeed, even if no variable with 'name' exists.
const TVariable *getNamedVariable(const TSourceLoc &location,
const ImmutableString &name,
const TSymbol *symbol);
TIntermTyped *parseVariableIdentifier(const TSourceLoc &location,
const ImmutableString &name,
const TSymbol *symbol);
// Look at a '.' field selector string and change it into offsets for a vector.
bool parseVectorFields(const TSourceLoc &line,
const ImmutableString &compString,
uint32_t vecSize,
TVector<uint32_t> *fieldOffsets);
void assignError(const TSourceLoc &line, const char *op, const TType &left, const TType &right);
void unaryOpError(const TSourceLoc &line, const char *op, const TType &operand);
void binaryOpError(const TSourceLoc &line,
const char *op,
const TType &left,
const TType &right);
// Check functions - the ones that return bool return false if an error was generated.
void checkIsValidExpressionStatement(const TSourceLoc &line, TIntermTyped *expr);
bool checkIsNotReserved(const TSourceLoc &line, const ImmutableString &identifier);
void checkPrecisionSpecified(const TSourceLoc &line, TPrecision precision, TBasicType type);
bool checkCanBeLValue(const TSourceLoc &line, const char *op, TIntermTyped *node);
void checkIsConst(TIntermTyped *node);
void checkIsScalarInteger(TIntermTyped *node, const char *token);
bool checkIsAtGlobalLevel(const TSourceLoc &line, const char *token);
bool checkConstructorArguments(const TSourceLoc &line,
const TIntermSequence &arguments,
const TType &type);
// Returns a sanitized array size to use (the size is at least 1).
unsigned int checkIsValidArraySize(const TSourceLoc &line, TIntermTyped *expr);
bool checkIsValidArrayDimension(const TSourceLoc &line, TVector<unsigned int> *arraySizes);
bool checkIsValidQualifierForArray(const TSourceLoc &line, const TPublicType &elementQualifier);
bool checkArrayElementIsNotArray(const TSourceLoc &line, const TPublicType &elementType);
bool checkArrayOfArraysInOut(const TSourceLoc &line,
const TPublicType &elementType,
const TType &arrayType);
bool checkIsNonVoid(const TSourceLoc &line,
const ImmutableString &identifier,
const TBasicType &type);
bool checkIsScalarBool(const TSourceLoc &line, const TIntermTyped *type);
void checkIsScalarBool(const TSourceLoc &line, const TPublicType &pType);
bool checkIsNotOpaqueType(const TSourceLoc &line,
const TTypeSpecifierNonArray &pType,
const char *reason);
void checkDeclaratorLocationIsNotSpecified(const TSourceLoc &line, const TPublicType &pType);
void checkLocationIsNotSpecified(const TSourceLoc &location,
const TLayoutQualifier &layoutQualifier);
void checkStd430IsForShaderStorageBlock(const TSourceLoc &location,
const TLayoutBlockStorage &blockStorage,
const TQualifier &qualifier);
// Check if at least one of the specified extensions can be used, and generate error/warning as
// appropriate according to the spec.
// This function is only needed for a few different small constant sizes of extension array, and
// we want to avoid unnecessary dynamic allocations. That's why checkCanUseOneOfExtensions is a
// template function rather than one taking a vector.
template <size_t size>
bool checkCanUseOneOfExtensions(const TSourceLoc &line,
const std::array<TExtension, size> &extensions);
bool checkCanUseExtension(const TSourceLoc &line, TExtension extension);
// Done for all declarations, whether empty or not.
void declarationQualifierErrorCheck(const sh::TQualifier qualifier,
const sh::TLayoutQualifier &layoutQualifier,
const TSourceLoc &location);
// Done for the first non-empty declarator in a declaration.
void nonEmptyDeclarationErrorCheck(const TPublicType &publicType,
const TSourceLoc &identifierLocation);
// Done only for empty declarations.
void emptyDeclarationErrorCheck(const TType &type, const TSourceLoc &location);
void checkCanUseLayoutQualifier(const TSourceLoc &location);
bool checkLayoutQualifierSupported(const TSourceLoc &location,
const ImmutableString &layoutQualifierName,
int versionRequired);
bool checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location,
const TLayoutQualifier &layoutQualifier);
void functionCallRValueLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *fnCall);
void checkInvariantVariableQualifier(bool invariant,
const TQualifier qualifier,
const TSourceLoc &invariantLocation);
void checkInputOutputTypeIsValidES3(const TQualifier qualifier,
const TPublicType &type,
const TSourceLoc &qualifierLocation);
void checkLocalVariableConstStorageQualifier(const TQualifierWrapperBase &qualifier);
void checkTCSOutVarIndexIsValid(TIntermBinary *binaryExpression, const TSourceLoc &location);
void checkAdvancedBlendEquationsNotSpecified(
const TSourceLoc &location,
const AdvancedBlendEquations &advancedBlendEquations,
const TQualifier &qualifier);
const TPragma &pragma() const { return mDirectiveHandler.pragma(); }
const TExtensionBehavior &extensionBehavior() const
{
return mDirectiveHandler.extensionBehavior();
}
bool isExtensionEnabled(TExtension extension) const;
void handleExtensionDirective(const TSourceLoc &loc, const char *extName, const char *behavior);
void handlePragmaDirective(const TSourceLoc &loc,
const char *name,
const char *value,
bool stdgl);
// For built-ins that can be redeclared, adjusts the type qualifier so transformations can
// identify them correctly.
void adjustRedeclaredBuiltInType(const TSourceLoc &line,
const ImmutableString &identifier,
TType *type);
// Returns true on success. *initNode may still be nullptr on success in case the initialization
// is not needed in the AST.
bool executeInitializer(const TSourceLoc &line,
const ImmutableString &identifier,
TType *type,
TIntermTyped *initializer,
TIntermBinary **initNode);
TIntermNode *addConditionInitializer(const TPublicType &pType,
const ImmutableString &identifier,
TIntermTyped *initializer,
const TSourceLoc &loc);
void beginNestedScope();
void endNestedScope();
void beginLoop(TLoopType loopType, const TSourceLoc &line);
void onLoopConditionBegin(TIntermNode *init, const TSourceLoc &line);
void onLoopConditionEnd(TIntermNode *condition, const TSourceLoc &line);
void onLoopContinueEnd(TIntermNode *statement, const TSourceLoc &line);
void onDoLoopBegin();
void onDoLoopConditionBegin();
TIntermNode *addLoop(TLoopType type,
TIntermNode *init,
TIntermNode *cond,
TIntermTyped *expr,
TIntermNode *body,
const TSourceLoc &loc);
// For "if" test nodes. There are three children: a condition, a true path, and a false path.
// The two paths are in TIntermNodePair code.
void onIfTrueBlockBegin(TIntermTyped *cond, const TSourceLoc &loc);
void onIfTrueBlockEnd();
void onIfFalseBlockBegin();
void onIfFalseBlockEnd();
TIntermNode *addIfElse(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &loc);
void addFullySpecifiedType(TPublicType *typeSpecifier);
TPublicType addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
const TPublicType &typeSpecifier);
TIntermDeclaration *parseSingleDeclaration(TPublicType &publicType,
const TSourceLoc &identifierOrTypeLocation,
const ImmutableString &identifier);
TIntermDeclaration *parseSingleArrayDeclaration(TPublicType &elementType,
const TSourceLoc &identifierLocation,
const ImmutableString &identifier,
const TSourceLoc &indexLocation,
const TVector<unsigned int> &arraySizes);
TIntermDeclaration *parseSingleInitDeclaration(const TPublicType &publicType,
const TSourceLoc &identifierLocation,
const ImmutableString &identifier,
const TSourceLoc &initLocation,
TIntermTyped *initializer);
// Parse a declaration like "type a[n] = initializer"
// Note that this does not apply to declarations like "type[n] a = initializer"
TIntermDeclaration *parseSingleArrayInitDeclaration(TPublicType &elementType,
const TSourceLoc &identifierLocation,
const ImmutableString &identifier,
const TSourceLoc &indexLocation,
const TVector<unsigned int> &arraySizes,
const TSourceLoc &initLocation,
TIntermTyped *initializer);
TIntermGlobalQualifierDeclaration *parseGlobalQualifierDeclaration(
const TTypeQualifierBuilder &typeQualifierBuilder,
const TSourceLoc &identifierLoc,
const ImmutableString &identifier,
const TSymbol *symbol);
void parseDeclarator(TPublicType &publicType,
const TSourceLoc &identifierLocation,
const ImmutableString &identifier,
TIntermDeclaration *declarationOut);
void parseArrayDeclarator(TPublicType &elementType,
const TSourceLoc &identifierLocation,
const ImmutableString &identifier,
const TSourceLoc &arrayLocation,
const TVector<unsigned int> &arraySizes,
TIntermDeclaration *declarationOut);
void parseInitDeclarator(const TPublicType &publicType,
const TSourceLoc &identifierLocation,
const ImmutableString &identifier,
const TSourceLoc &initLocation,
TIntermTyped *initializer,
TIntermDeclaration *declarationOut);
// Parse a declarator like "a[n] = initializer"
void parseArrayInitDeclarator(const TPublicType &elementType,
const TSourceLoc &identifierLocation,
const ImmutableString &identifier,
const TSourceLoc &indexLocation,
const TVector<unsigned int> &arraySizes,
const TSourceLoc &initLocation,
TIntermTyped *initializer,
TIntermDeclaration *declarationOut);
TIntermNode *addEmptyStatement(const TSourceLoc &location);
void parseDefaultPrecisionQualifier(const TPrecision precision,
const TPublicType &type,
const TSourceLoc &loc);
void parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder);
TIntermFunctionPrototype *addFunctionPrototypeDeclaration(const TFunction &parsedFunction,
const TSourceLoc &location);
TIntermFunctionDefinition *addFunctionDefinition(TIntermFunctionPrototype *functionPrototype,
TIntermBlock *functionBody,
const TSourceLoc &location);
void parseFunctionDefinitionHeader(const TSourceLoc &location,
const TFunction *function,
TIntermFunctionPrototype **prototypeOut);
TFunction *parseFunctionDeclarator(const TSourceLoc &location, TFunction *function);
TFunction *parseFunctionHeader(const TPublicType &type,
const ImmutableString &name,
const TSourceLoc &location);
TFunctionLookup *addNonConstructorFunc(const ImmutableString &name, const TSymbol *symbol);
TFunctionLookup *addConstructorFunc(const TPublicType &publicType);
TParameter parseParameterDeclarator(const TPublicType &type,
const ImmutableString &name,
const TSourceLoc &nameLoc);
TParameter parseParameterArrayDeclarator(const TPublicType &elementType,
const ImmutableString &name,
const TSourceLoc &nameLoc,
TVector<unsigned int> *arraySizes,
const TSourceLoc &arrayLoc);
void parseParameterQualifier(const TSourceLoc &line,
const TTypeQualifierBuilder &typeQualifierBuilder,
TPublicType &type);
void addParameter(TFunction *function, TParameter *param);
TIntermTyped *addIndexExpression(TIntermTyped *baseExpression,
const TSourceLoc &location,
TIntermTyped *indexExpression);
TIntermTyped *addFieldSelectionExpression(TIntermTyped *baseExpression,
const TSourceLoc &dotLocation,
const ImmutableString &fieldString,
const TSourceLoc &fieldLocation);
// Parse declarator for a single field
TDeclarator *parseStructDeclarator(const ImmutableString &identifier, const TSourceLoc &loc);
TDeclarator *parseStructArrayDeclarator(const ImmutableString &identifier,
const TSourceLoc &loc,
const TVector<unsigned int> *arraySizes);
void checkDoesNotHaveDuplicateFieldNames(const TFieldList *fields, const TSourceLoc &location);
void checkDoesNotHaveTooManyFields(const ImmutableString &name,
const TFieldList *fields,
const TSourceLoc &location);
TFieldList *addStructFieldList(TFieldList *fields, const TSourceLoc &location);
TFieldList *combineStructFieldLists(TFieldList *processedFields,
const TFieldList *newlyAddedFields,
const TSourceLoc &location);
TFieldList *addStructDeclaratorListWithQualifiers(
const TTypeQualifierBuilder &typeQualifierBuilder,
TPublicType *typeSpecifier,
const TDeclaratorList *declaratorList);
TFieldList *addStructDeclaratorList(const TPublicType &typeSpecifier,
const TDeclaratorList *declaratorList);
TTypeSpecifierNonArray addStructure(const TSourceLoc &structLine,
const TSourceLoc &nameLine,
const ImmutableString &structName,
TFieldList *fieldList);
TIntermDeclaration *addInterfaceBlock(const TTypeQualifierBuilder &typeQualifierBuilder,
const TSourceLoc &nameLine,
const ImmutableString &blockName,
TFieldList *fieldList,
const ImmutableString &instanceName,
const TSourceLoc &instanceLine,
const TVector<unsigned int> *arraySizes,
const TSourceLoc &arraySizesLine);
void parseLocalSize(const ImmutableString &qualifierType,
const TSourceLoc &qualifierTypeLine,
int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
size_t index,
sh::WorkGroupSize *localSize);
void parseNumViews(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *numViews);
void parseInvocations(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *numInvocations);
void parseMaxVertices(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *numMaxVertices);
void parseVertices(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *numVertices);
void parseIndexLayoutQualifier(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *index);
TLayoutQualifier parseLayoutQualifier(const ImmutableString &qualifierType,
const TSourceLoc &qualifierTypeLine);
TLayoutQualifier parseLayoutQualifier(const ImmutableString &qualifierType,
const TSourceLoc &qualifierTypeLine,
int intValue,
const TSourceLoc &intValueLine);
TTypeQualifierBuilder *createTypeQualifierBuilder(const TSourceLoc &loc);
TStorageQualifierWrapper *parseGlobalStorageQualifier(TQualifier qualifier,
const TSourceLoc &loc);
TStorageQualifierWrapper *parseVaryingQualifier(const TSourceLoc &loc);
TStorageQualifierWrapper *parseInQualifier(const TSourceLoc &loc);
TStorageQualifierWrapper *parseOutQualifier(const TSourceLoc &loc);
TStorageQualifierWrapper *parseInOutQualifier(const TSourceLoc &loc);
TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier,
TLayoutQualifier rightQualifier,
const TSourceLoc &rightQualifierLocation);
// Performs an error check for embedded struct declarations.
void enterStructDeclaration(const TSourceLoc &line, const ImmutableString &identifier);
void exitStructDeclaration();
void checkIsBelowStructNestingLimit(const TSourceLoc &line, const TField &field);
void beginSwitch(const TSourceLoc &line, TIntermTyped *init);
TIntermSwitch *addSwitch(TIntermTyped *init,
TIntermBlock *statementList,
const TSourceLoc &loc);
TIntermCase *addCase(TIntermTyped *condition, const TSourceLoc &loc);
TIntermCase *addDefault(const TSourceLoc &loc);
TIntermTyped *addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc);
TIntermTyped *addUnaryMathLValue(TOperator op, TIntermTyped *child, const TSourceLoc &loc);
TIntermTyped *addBinaryMath(TOperator op,
TIntermTyped *left,
TIntermTyped *right,
const TSourceLoc &loc);
TIntermTyped *addBinaryMathBooleanResult(TOperator op,
TIntermTyped *left,
TIntermTyped *right,
const TSourceLoc &loc);
TIntermTyped *addAssign(TOperator op,
TIntermTyped *left,
TIntermTyped *right,
const TSourceLoc &loc);
void onShortCircuitAndBegin(TIntermTyped *left, const TSourceLoc &loc);
void onShortCircuitOrBegin(TIntermTyped *left, const TSourceLoc &loc);
void onCommaLeftHandSideParsed(TIntermTyped *left);
TIntermTyped *addComma(TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
TIntermBranch *addBranch(TOperator op, const TSourceLoc &loc);
TIntermBranch *addBranch(TOperator op, TIntermTyped *expression, const TSourceLoc &loc);
void appendStatement(TIntermBlock *block, TIntermNode *statement);
void checkTextureGather(TIntermAggregate *functionCall);
void checkTextureOffset(TIntermAggregate *functionCall);
void checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall);
void checkImageMemoryAccessForUserDefinedFunctions(const TFunction *functionDefinition,
const TIntermAggregate *functionCall);
void checkAtomicMemoryBuiltinFunctions(TIntermAggregate *functionCall);
void checkInterpolationFS(TIntermAggregate *functionCall);
// fnCall is only storing the built-in op, and function name or constructor type. arguments
// has the arguments.
TIntermTyped *addFunctionCallOrMethod(TFunctionLookup *fnCall, const TSourceLoc &loc);
void onTernaryConditionParsed(TIntermTyped *cond, const TSourceLoc &line);
void onTernaryTrueExpressionParsed(TIntermTyped *trueExpression, const TSourceLoc &line);
TIntermTyped *addTernarySelection(TIntermTyped *cond,
TIntermTyped *trueExpression,
TIntermTyped *falseExpression,
const TSourceLoc &line);
uint32_t getClipDistanceArraySize() const
{
return mClipDistanceInfo.size > 0 ? mClipDistanceInfo.size : mClipDistanceInfo.maxIndex + 1;
}
uint32_t getCullDistanceArraySize() const
{
return mCullDistanceInfo.size > 0 ? mCullDistanceInfo.size : mCullDistanceInfo.maxIndex + 1;
}
bool isClipDistanceRedeclared() const { return mClipDistanceInfo.size > 0; }
bool isCullDistanceRedeclared() const { return mCullDistanceInfo.size > 0; }
bool isClipDistanceUsed() const
{
return mClipDistanceInfo.maxIndex >= 0 || mClipDistanceInfo.hasNonConstIndex;
}
int getGeometryShaderMaxVertices() const { return mGeometryShaderMaxVertices; }
int getGeometryShaderInvocations() const
{
return (mGeometryShaderInvocations > 0) ? mGeometryShaderInvocations : 1;
}
TLayoutPrimitiveType getGeometryShaderInputPrimitiveType() const
{
return mGeometryShaderInputPrimitiveType;
}
TLayoutPrimitiveType getGeometryShaderOutputPrimitiveType() const
{
return mGeometryShaderOutputPrimitiveType;
}
int getTessControlShaderOutputVertices() const { return mTessControlShaderOutputVertices; }
TLayoutTessEvaluationType getTessEvaluationShaderInputPrimitiveType() const
{
return mTessEvaluationShaderInputPrimitiveType;
}
TLayoutTessEvaluationType getTessEvaluationShaderInputVertexSpacingType() const
{
return mTessEvaluationShaderInputVertexSpacingType;
}
TLayoutTessEvaluationType getTessEvaluationShaderInputOrderingType() const
{
return mTessEvaluationShaderInputOrderingType;
}
TLayoutTessEvaluationType getTessEvaluationShaderInputPointType() const
{
return mTessEvaluationShaderInputPointType;
}
void markShaderHasPrecise() { mHasAnyPreciseType = true; }
bool hasAnyPreciseType() const { return mHasAnyPreciseType; }
AdvancedBlendEquations getAdvancedBlendEquations() const { return mAdvancedBlendEquations; }
ShShaderOutput getOutputType() const { return mOutputType; }
// Pop the side effect of a statement when it's discarded, like when ; is encountered.
void endStatementWithValue(TIntermNode *statement);
bool postParseChecks();
const ShCompileOptions &getCompileOptions() const { return mCompileOptions; }
// TODO(jmadill): make this private
TSymbolTable &symbolTable; // symbol table that goes with the language currently being parsed
private:
class AtomicCounterBindingState;
constexpr static size_t kAtomicCounterSize = 4;
// UNIFORM_ARRAY_STRIDE for atomic counter arrays is an implementation-dependent value which
// can be queried after a program is linked according to ES 3.10 section 7.7.1. This is
// controversial with the offset inheritance as described in ESSL 3.10 section 4.4.6. Currently
// we treat it as always 4 in favour of the original interpretation in
// "ARB_shader_atomic_counters".
// TODO(jie.a.chen@intel.com): Double check this once the spec vagueness is resolved.
// Note that there may be tests in AtomicCounter_test that will need to be updated as well.
constexpr static size_t kAtomicCounterArrayStride = 4;
void markStaticUseIfSymbol(TIntermNode *node);
// Returns a clamped index. If it prints out an error message, the token is "[]".
int checkIndexLessThan(bool outOfRangeIndexIsError,
const TSourceLoc &location,
int index,
unsigned int arraySize,
const char *reason);
bool declareVariable(const TSourceLoc &line,
const ImmutableString &identifier,
const TType *type,
GeomTessArray sized,
TVariable **variable);
void checkNestingLevel(const TSourceLoc &line);
bool checkCase(const TSourceLoc &line, int64_t caseValue, const char *caseOrDefault);
void checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line,
const ImmutableString &identifier,
TType *type);
void checkDeclarationIsValidArraySize(const TSourceLoc &line,
const ImmutableString &identifier,
TType *type);
bool checkIsValidTypeAndQualifierForArray(const TSourceLoc &indexLocation,
const TPublicType &elementType);
// Done for all atomic counter declarations, whether empty or not.
void atomicCounterQualifierErrorCheck(const TPublicType &publicType,
const TSourceLoc &location);
// Assumes that multiplication op has already been set based on the types.
bool isMultiplicationTypeCombinationValid(TOperator op, const TType &left, const TType &right);
void checkInternalFormatIsNotSpecified(const TSourceLoc &location,
TLayoutImageInternalFormat internalFormat);
void checkMemoryQualifierIsNotSpecified(const TMemoryQualifier &memoryQualifier,
const TSourceLoc &location);
void checkAtomicCounterOffsetIsValid(bool forceAppend, const TSourceLoc &loc, TType *type);
void checkAtomicCounterOffsetDoesNotOverlap(bool forceAppend,
const TSourceLoc &loc,
TType *type);
void checkAtomicCounterOffsetAlignment(const TSourceLoc &location, const TType &type);
void checkAtomicCounterOffsetLimit(const TSourceLoc &location, const TType &type);
void checkIndexIsNotSpecified(const TSourceLoc &location, int index);
void checkBindingIsValid(const TSourceLoc &identifierLocation, const TType &type);
void checkBindingIsNotSpecified(const TSourceLoc &location, int binding);
void checkOffsetIsNotSpecified(const TSourceLoc &location, int offset);
void checkImageBindingIsValid(const TSourceLoc &location,
int binding,
int arrayTotalElementCount);
void checkSamplerBindingIsValid(const TSourceLoc &location,
int binding,
int arrayTotalElementCount);
void checkBlockBindingIsValid(const TSourceLoc &location,
const TQualifier &qualifier,
int binding,
int arraySize);
void checkAtomicCounterBindingIsValid(const TSourceLoc &location, int binding);
void checkPixelLocalStorageBindingIsValid(const TSourceLoc &, const TType &);
void checkUniformLocationInRange(const TSourceLoc &location,
int objectLocationCount,
const TLayoutQualifier &layoutQualifier);
void checkAttributeLocationInRange(const TSourceLoc &location,
int objectLocationCount,
const TLayoutQualifier &layoutQualifier);
void checkDepthIsNotSpecified(const TSourceLoc &location, TLayoutDepth depth);
void checkYuvIsNotSpecified(const TSourceLoc &location, bool yuv);
void checkEarlyFragmentTestsIsNotSpecified(const TSourceLoc &location, bool earlyFragmentTests);
void checkNoncoherentIsSpecified(const TSourceLoc &location, bool noncoherent);
void checkNoncoherentIsNotSpecified(const TSourceLoc &location, bool noncoherent);
bool checkUnsizedArrayConstructorArgumentDimensionality(const TIntermSequence &arguments,
TType type,
const TSourceLoc &line);
// Check texture offset is within range.
void checkSingleTextureOffset(const TSourceLoc &line,
const TConstantUnion *values,
size_t size,
int minOffsetValue,
int maxOffsetValue);
// Will set the size of the outermost array according to geometry shader input layout.
void checkGeometryShaderInputAndSetArraySize(const TSourceLoc &location,
const ImmutableString &token,
TType *type,
GeomTessArray *sizedOut);
// Similar, for tessellation shaders.
void checkTessellationShaderUnsizedArraysAndSetSize(const TSourceLoc &location,
const ImmutableString &token,
TType *type,
GeomTessArray *sizedOut);
// Will size any unsized array type so unsized arrays won't need to be taken into account
// further along the line in parsing.
void checkIsNotUnsizedArray(const TSourceLoc &line,
const char *errorMessage,
const ImmutableString &token,
TType *arrayType);
TIntermTyped *addBinaryMathInternal(TOperator op,
TIntermTyped *left,
TIntermTyped *right,
const TSourceLoc &loc);
TIntermTyped *createUnaryMath(TOperator op,
TIntermTyped *child,
const TSourceLoc &loc,
const TFunction *func);
TIntermTyped *addMethod(TFunctionLookup *fnCall, const TSourceLoc &loc);
TIntermTyped *addConstructor(TFunctionLookup *fnCall, const TSourceLoc &line);
TIntermTyped *addNonConstructorFunctionCallImpl(TFunctionLookup *fnCall, const TSourceLoc &loc);
TIntermTyped *addNonConstructorFunctionCall(TFunctionLookup *fnCall, const TSourceLoc &loc);
// Return either the original expression or the folded version of the expression in case the
// folded node will validate the same way during subsequent parsing.
TIntermTyped *expressionOrFoldedResult(TIntermTyped *expression);
// Return true if the checks pass
bool binaryOpCommonCheck(TOperator op,
TIntermTyped *left,
TIntermTyped *right,
const TSourceLoc &loc);
TIntermFunctionPrototype *createPrototypeNodeFromFunction(const TFunction &function,
const TSourceLoc &location,
bool insertParametersToSymbolTable);
void checkESSL100ForLoopInit(TIntermNode *init, const TSourceLoc &line);
void checkESSL100ForLoopCondition(TIntermNode *condition, const TSourceLoc &line);
void checkESSL100ForLoopContinue(TIntermNode *statement, const TSourceLoc &line);
void checkESSL100NoLoopSymbolAssign(TIntermSymbol *symbol, const TSourceLoc &line);
void checkESSL100ConstantIndex(TIntermTyped *index, const TSourceLoc &line);
bool isESSL100ConstantLoopSymbol(TIntermSymbol *symbol);
void checkCallGraph();
void setAtomicCounterBindingDefaultOffset(const TPublicType &declaration,
const TSourceLoc &location);
bool checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier &typeQualifier);
bool parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier);
bool parseGeometryShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier);
void setGeometryShaderInputArraySize(unsigned int inputArraySize, const TSourceLoc &line);
bool parseTessControlShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier);
bool parseTessEvaluationShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier);
void checkVariableSize(const TSourceLoc &line,
const ImmutableString &identifier,
const TType *type);
void checkVaryingLocations(const TSourceLoc &line, const TVariable *variable);
void checkFragmentOutputLocations(const TSourceLoc &line, const TVariable *variable);
void checkVariableLocations(const TSourceLoc &line, const TVariable *variable);
void postParseValidateFragmentOutputLocations();
void sizeUnsizedArrayTypes(uint32_t arraySize);
enum class ControlFlowType
{
// Control flow nested under `if`.
If,
// Control flow nested under `for`, `while` or `do { ... } while`.
Loop,
// Control flow nested under `switch`.
Switch,
// Not a divergent control flow, but nested under a new `{}` scope.
NewScope,
};
bool isNestedIn(ControlFlowType type) const;
bool isDirectlyUnderSwitch() const;
void popControlFlow();
// Used to derive the IR type id of TType's that are statically allocated, which (currently)
// don't have an assigned type id. Once IR is the only path, static TTypes (used to bake the
// built-in variables and functions) can be simplified and the ID predefined and included with
// it.
ir::TypeId getTypeId(const TType &type);
// For built-ins, declare them in the IR on first use.
ir::VariableId declareBuiltInOnFirstUse(const TVariable *variable);
// Declare the variable to IR on declaration, or in the case of unsized geometry/tessellation
// arrays, whenever the size is determined.
void declareIRVariable(const TVariable *variable, GeomTessArray sized);
// Declare the function to the IR builder. If it's a definition and a prototype was previously
// seen, the parameter names are updated instead.
void declareFunction(const TFunction *function, FunctionDeclaration declaration);
// Push a variable to the IR builder.
void pushVariable(const TVariable *variable);
// Push a constant to the IR builder.
const TConstantUnion *pushConstant(const TConstantUnion *constant, const TType &type);
// Certain operations become illegal only iff the shader declares pixel local storage uniforms.
enum class PLSIllegalOperations
{
// When polyfilled with shader images, pixel local storage requires early_fragment_tests,
// which causes discard to interact differently with the depth and stencil tests.
//
// To ensure identical behavior across all backends (some of which may not have access to
// early_fragment_tests), we disallow discard if pixel local storage uniforms have been
// declared.
Discard,
// ARB_fragment_shader_interlock functions cannot be called within flow control, which
// includes any code that might execute after a return statement. To keep things simple, and
// since these "interlock" calls are automatically injected by the compiler inside of
// main(), we disallow return from main() if pixel local storage uniforms have been
// declared.
ReturnFromMain,
// When polyfilled with shader images, pixel local storage requires early_fragment_tests,
// which causes assignments to gl_FragDepth(EXT) and gl_SampleMask to be ignored.
//
// To ensure identical behavior across all backends, we disallow assignment to these values
// if pixel local storage uniforms have been declared.
AssignFragDepth,
AssignSampleMask,
// EXT_blend_func_extended may restrict the number of draw buffers with a nonzero output
// index, which can invalidate a PLS implementation.
FragDataIndexNonzero,
// KHR_blend_equation_advanced is incompatible with multiple draw buffers, which is a
// required feature for many PLS implementations.
EnableAdvancedBlendEquation,
};
// Generates an error if any pixel local storage uniforms have been declared (more specifically,
// if mPLSLayouts is not empty).
//
// If no pixel local storage uniforms have been declared, and if the PLS extension is enabled,
// saves the potential error to mPLSPotentialErrors in case we encounter a PLS uniform later.
void errorIfPLSDeclared(const TSourceLoc &, PLSIllegalOperations);
// Set to true when the last/current declarator list was started with an empty declaration. The
// non-empty declaration error check will need to be performed if the empty declaration is
// followed by a declarator.
bool mDeferredNonEmptyDeclarationErrorCheck;
sh::GLenum mShaderType; // vertex/fragment/geometry/etc shader
ShShaderSpec mShaderSpec; // The language specification compiler conforms to - GLES/WebGL/etc.
ShCompileOptions mCompileOptions; // Options passed to TCompiler
const ShBuiltInResources &mResources; // Limits passed to TCompiler
int mShaderVersion;
TIntermBlock *mTreeRoot; // root of parse tree being created
int mStructNestingLevel; // incremented while parsing a struct declaration
const TFunction *mCurrentFunction; // the function that's currently being parsed
bool mFunctionReturnsValue; // true if a non-void function has a return
bool mEarlyFragmentTestsSpecified; // true if layout(early_fragment_tests) in; is specified.
bool mHasDiscard; // true if |discard| is encountered in the shader.
bool mSampleQualifierSpecified; // true if the |sample| qualifier is used
bool mPositionRedeclaredForSeparateShaderObject; // true if EXT_separate_shader_objects is
// enabled and gl_Position is redefined.
bool mPointSizeRedeclaredForSeparateShaderObject; // true if EXT_separate_shader_objects is
// enabled and gl_PointSize is redefined.
bool mPositionOrPointSizeUsedForSeparateShaderObject; // true if gl_Position or gl_PointSize
// has been referenced.
bool mUsesDerivatives; // true if screen-space derivatives are used implicitly or explicitly
TLayoutMatrixPacking mDefaultUniformMatrixPacking;
TLayoutBlockStorage mDefaultUniformBlockStorage;
TLayoutMatrixPacking mDefaultBufferMatrixPacking;
TLayoutBlockStorage mDefaultBufferBlockStorage;
TString mHashErrMsg;
TDiagnostics *mDiagnostics;
TDirectiveHandler mDirectiveHandler;
angle::pp::Preprocessor mPreprocessor;
void *mScanner;
// Keep track of clip/cull distance redeclaration, accessed indices, etc so that gl_ClipDistance
// and gl_CullDistance can be validated and sized at the end of compilation.
ClipCullDistanceInfo mClipDistanceInfo;
ClipCullDistanceInfo mCullDistanceInfo;
// Keep track of local group size declared in layout. It should be declared only once.
bool mComputeShaderLocalSizeDeclared;
sh::WorkGroupSize mComputeShaderLocalSize;
// Keep track of number of views declared in layout.
int mNumViews;
// Maximum number of uniform blocks allowed to be declared in this shader. Taken from the
// built-in resources and resolved to this shader type.
unsigned int mMaxUniformBlocks;
// Current count of declared uniform blocks.
unsigned int mNumUniformBlocks;
// Keeps track of whether any of the built-ins that can be redeclared (see
// IsRedeclarableBuiltIn()) has been marked as invariant/precise before the possible
// redeclaration.
//
// If redeclared after being marked as invariant/precise, a compile error is generated.
// The GLSL spec does not explicitly call this out as invalid, but it's not a useful sequence
// of statements (invariant/precise could have been directly specified on the redeclaration),
// and there are no known users.
TUnorderedMap<TQualifier, bool> mBuiltInQualified;
// Keeps track whether we are declaring / defining a function
bool mDeclaringFunction;
// Keeps track whether we are declaring / defining the function main().
bool mDeclaringMain;
const TFunction *mMainFunction;
// Whether `return` has been observed in `main()`. Used to validate barrier() in tessellation
// control shaders which are not allowed after `return`.
bool mIsReturnVisitedInMain;
// Keeps track of the total size of shader-private variables, if validating that this size
// should not exceed a sensible threshold.
angle::base::CheckedNumeric<size_t> mTotalPrivateVariablesSize;
// Track state related to control flow, used for various validation:
//
// * That case is within switch, continue is within loop, and break is within loop or switch
// * That the shader statements don't get too nested (based on `MaxStatementDepth`)
// * In tessellation control shaders, barrier() cannot be called in divergent control flow.
// * ESSL 1.0 limits restricts the shape of `for` loops (see Appendix A)
// * ESSL 1.0 limits array indices to `constant-index-expressions` (see Appendix A)
// * Rejection of obvious infinite loops with WebGL.
struct ControlFlow
{
ControlFlowType type;
// Used when validating ESSL 1.0 limitations for `for` loops.
TSymbolUniqueId forLoopSymbol = TSymbolUniqueId::kInvalid();
bool isForLoopSymbolConstant = false;
// Used to detect and reject infinite loops with WebGL.
TSourceLoc loopLocation = kNoSourceLoc;
bool isLoopConditionConstantTrue = false;
const TVariable *loopConditionConstantTrueSymbol = nullptr;
bool hasBreak = false;
bool hasReturn = false;
// Used to detect and reject invalid `case` placements in a switch.
// int64_t is used to include both signed and unsigned case values (which are 32-bit). The
// default case uses a number outside the [INT_MIN, UINT_MAX] range.
TBasicType switchType = EbtInt;
static constexpr int64_t kDefaultCaseLabel = std::numeric_limits<int64_t>::max();
TVector<int64_t> caseLabels;
};
std::vector<ControlFlow> mControlFlow;
// Whether ESSL 1.0 limitations in Appendix A must be enforced.
bool mValidateESSL100Limitations;
// Whether the variable is initialized to true, and never modified. If this is used as a loop
// variable, where the loop doesn't have break or return, at the end of parse we can detect
// these loops as infinite loop.
TUnorderedSet<TSymbolUniqueId> mConstantTrueVariables;
TVector<VariableAndLocation> mPossiblyInfiniteLoops;
// Track the static call graph. Static recursion is disallowed by GLSL.
TUnorderedMap<const TFunction *, TUnorderedSet<const TFunction *>> mCallGraph;
// Track functions that have been defined. At the end of parse, if any
// function is called that's not in this list, it's a compile error.
TUnorderedSet<const TFunction *> mDefinedFunctions;
// Track the state of each atomic counter binding.
std::map<int, AtomicCounterBindingState> mAtomicCounterBindingStates;
// Track the layout qualifier of each pixel local storage binding.
std::map<int, ShPixelLocalStorageLayout> mPLSLayouts;
// Potential errors to generate immediately upon encountering a pixel local storage uniform.
std::vector<std::tuple<const TSourceLoc, PLSIllegalOperations>> mPLSPotentialErrors;
// Track the locations used by input and output varyings to detect conflicts.
LocationValidationMap mInputVaryingLocations;
LocationValidationMap mOutputVaryingLocations;
// Track the locations used by fragment shader outputs to detect conflicts.
TVector<VariableAndLocation> mFragmentOutputsWithLocation;
TVector<VariableAndLocation> mFragmentOutputsWithoutLocation;
TVector<VariableAndLocation> mFragmentOutputsYuv;
bool mFragmentOutputIndex1Used;
bool mFragmentOutputFragDepthUsed;
// Track the geometry shader global parameters declared in layout.
TLayoutPrimitiveType mGeometryShaderInputPrimitiveType;
TLayoutPrimitiveType mGeometryShaderOutputPrimitiveType;
int mGeometryShaderInvocations;
int mGeometryShaderMaxVertices;
unsigned int mGeometryInputArraySize;
int mTessControlShaderOutputVertices;
TLayoutTessEvaluationType mTessEvaluationShaderInputPrimitiveType;
TLayoutTessEvaluationType mTessEvaluationShaderInputVertexSpacingType;
TLayoutTessEvaluationType mTessEvaluationShaderInputOrderingType;
TLayoutTessEvaluationType mTessEvaluationShaderInputPointType;
// List of array declarations without an explicit size that have come before layout(vertices=N).
// Once the vertex count is specified, these arrays are sized.
TVector<TType *> mDeferredArrayTypesToSize;
// For the IR, the variables themselves are declared late instead of having to go through a
// retype.
TVector<const TVariable *> mDeferredArrayVariablesToSize;
// Whether the |precise| keyword has been seen in the shader.
bool mHasAnyPreciseType;
AdvancedBlendEquations mAdvancedBlendEquations;
// Track when we add new scope for func body in ESSL 1.00 spec
bool mFunctionBodyNewScope;
ShShaderOutput mOutputType;
ir::Builder mIRBuilder;
// Support for creating the IR while the translator still has the option to not go through the
// IR path. Once AST generation during parse is removed, TParseContext can instead keep track
// of IDs directly, instead of TSymbol derivatives, together with an array-based mapping to
// validation info including type and variable data.
struct VariableToIdInfo
{
ir::VariableId id;
// For nameless interface blocks, the shader directly references the fields. The IR instead
// selects a field from the block variable, which is found in |id|.
static constexpr uint32_t kNoImplicitField = 0xFFFF'FFFF;
uint32_t implicitField = kNoImplicitField;
};
angle::HashMap<const TSymbol *, ir::TypeId> mSymbolToTypeId;
angle::HashMap<const TVariable *, VariableToIdInfo> mVariableToId;
angle::HashMap<const TFunction *, ir::FunctionId> mFunctionToId;
};
int PaParseStrings(angle::Span<const char *const> string,
const int length[],
TParseContext *context);
} // namespace sh
#endif // COMPILER_TRANSLATOR_PARSECONTEXT_H_