// // 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 &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 *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 *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 bool checkCanUseOneOfExtensions(const TSourceLoc &line, const std::array &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 &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 &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 &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 &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 *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 *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 *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); bool 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 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 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::max(); TVector caseLabels; }; std::vector 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 mConstantTrueVariables; TVector mPossiblyInfiniteLoops; // Track the static call graph. Static recursion is disallowed by GLSL. TUnorderedMap> 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 mDefinedFunctions; // Track the state of each atomic counter binding. std::map mAtomicCounterBindingStates; // Track the layout qualifier of each pixel local storage binding. std::map mPLSLayouts; // Potential errors to generate immediately upon encountering a pixel local storage uniform. std::vector> 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 mFragmentOutputsWithLocation; TVector mFragmentOutputsWithoutLocation; TVector 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 mDeferredArrayTypesToSize; // For the IR, the variables themselves are declared late instead of having to go through a // retype. TVector 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 mSymbolToTypeId; angle::HashMap mVariableToId; angle::HashMap mFunctionToId; }; int PaParseStrings(angle::Span string, const int length[], TParseContext *context); } // namespace sh #endif // COMPILER_TRANSLATOR_PARSECONTEXT_H_