// // 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. //
#ifdefined(ANGLE_FUZZER_CORPUS_OUTPUT_DIR) # include "common/hash_utils.h" # include "common/mathutil.h" #endif
namespace sh
{
namespace
{ // Helper that returns if a top-level node is unused. If it's a function, the function prototype is // returned as well. bool IsTopLevelNodeUnusedFunction(const CallDAG &callDag, const std::vector<TFunctionMetadata> &metadata,
TIntermNode *node, const TFunction **functionOut)
{ const TIntermFunctionPrototype *asFunctionPrototype = node->getAsFunctionPrototypeNode(); const TIntermFunctionDefinition *asFunctionDefinition = node->getAsFunctionDefinition();
size_t callDagIndex = callDag.findIndex((*functionOut)->uniqueId()); if (callDagIndex == CallDAG::InvalidIndex)
{ // This happens only for unimplemented prototypes which are thus unused
ASSERT(asFunctionPrototype); returntrue;
}
std::ostringstream o = sh::InitializeStream<std::ostringstream>();
o << ANGLE_FUZZER_CORPUS_OUTPUT_DIR << std::hex << std::setw(16) << std::setfill('0') << hash
<< ".sample";
std::string s = o.str();
// Must match the input format of the fuzzer
FILE *f = fopen(s.c_str(), "w");
fwrite(contents.data(), sizeof(char), contentsLength, f);
fclose(f);
} #endif// defined(ANGLE_FUZZER_CORPUS_OUTPUT_DIR)
} // anonymous namespace
size_t GetGlobalMaxTokenSize(ShShaderSpec spec)
{ // WebGL defines a max token length of 256, while ES2 leaves max token // size undefined. ES3 defines a max size of 1024 characters. switch (spec)
{ case SH_WEBGL_SPEC: return 256; default: return 1024;
}
}
int GetMaxUniformVectorsForShaderType(GLenum shaderType, const ShBuiltInResources &resources)
{ switch (shaderType)
{ case GL_VERTEX_SHADER: return resources.MaxVertexUniformVectors; case GL_FRAGMENT_SHADER: return resources.MaxFragmentUniformVectors;
// TODO (jiawei.shao@intel.com): check if we need finer-grained component counting case GL_COMPUTE_SHADER: return resources.MaxComputeUniformComponents / 4; case GL_GEOMETRY_SHADER_EXT: return resources.MaxGeometryUniformComponents / 4; default:
UNREACHABLE(); return -1;
}
}
class [[nodiscard]] TScopedSymbolTableLevel
{ public:
TScopedSymbolTableLevel(TSymbolTable *table) : mTable(table)
{
ASSERT(mTable->isEmpty());
mTable->push();
}
~TScopedSymbolTableLevel()
{ while (!mTable->isEmpty())
mTable->pop();
}
private:
TSymbolTable *mTable;
};
int GetMaxShaderVersionForSpec(ShShaderSpec spec)
{ switch (spec)
{ case SH_GLES2_SPEC: case SH_WEBGL_SPEC: return 100; case SH_GLES3_SPEC: case SH_WEBGL2_SPEC: return 300; case SH_GLES3_1_SPEC: case SH_WEBGL3_SPEC: return 310; case SH_GLES3_2_SPEC: return 320; case SH_GL_CORE_SPEC: case SH_GL_COMPATIBILITY_SPEC: return 460; default:
UNREACHABLE(); return 0;
}
}
bool usesFragColor = false; bool usesFragData = false; // This validation is a bit stricter than the spec - it's only an error to write to // both FragData and FragColor. But because it's better not to have reads from undefined // variables, we always return an error if they are both referenced, rather than only if they // are written. if (symbolTable.isStaticallyUsed(*BuiltInVariable::gl_FragColor()) ||
symbolTable.isStaticallyUsed(*BuiltInVariable::gl_SecondaryFragColorEXT()))
{
usesFragColor = true;
} // Extension variables may not always be initialized (saves some time at symbol table init). bool secondaryFragDataUsed =
symbolTable.gl_SecondaryFragDataEXT() != nullptr &&
symbolTable.isStaticallyUsed(*symbolTable.gl_SecondaryFragDataEXT()); if (symbolTable.isStaticallyUsed(*symbolTable.gl_FragData()) || secondaryFragDataUsed)
{
usesFragData = true;
} if (usesFragColor && usesFragData)
{ constchar *errorMessage = "cannot use both gl_FragData and gl_FragColor"; if (symbolTable.isStaticallyUsed(*BuiltInVariable::gl_SecondaryFragColorEXT()) ||
secondaryFragDataUsed)
{
errorMessage = "cannot use both output variable sets (gl_FragData, gl_SecondaryFragDataEXT)" " and (gl_FragColor, gl_SecondaryFragColorEXT)";
}
diagnostics->globalError(errorMessage); returnfalse;
} returntrue;
}
bool TCompiler::shouldRunLoopAndIndexingValidation(const ShCompileOptions &compileOptions) const
{ // If compiling an ESSL 1.00 shader for WebGL, or if its been requested through the API, // validate loop and indexing as well (to verify that the shader only uses minimal functionality // of ESSL 1.00 as in Appendix A of the spec). return (IsWebGLBasedSpec(mShaderSpec) && mShaderVersion == 100) ||
compileOptions.validateLoopIndexing;
}
bool TCompiler::shouldLimitTypeSizes() const
{ // WebGL shaders limit the size of variables' types in shaders, // including arrays, structs and interface blocks. return IsWebGLBasedSpec(mShaderSpec);
}
// Reset the extension behavior for each compilation unit.
ResetExtensionBehavior(mResources, mExtensionBehavior, compileOptions);
// If gl_DrawID is not supported, remove it from the available extensions // Currently we only allow emulation of gl_DrawID constbool glDrawIDSupported = compileOptions.emulateGLDrawID; if (!glDrawIDSupported)
{ auto it = mExtensionBehavior.find(TExtension::ANGLE_multi_draw); if (it != mExtensionBehavior.end())
{
mExtensionBehavior.erase(it);
}
}
constbool glBaseVertexBaseInstanceSupported = compileOptions.emulateGLBaseVertexBaseInstance; if (!glBaseVertexBaseInstanceSupported)
{ auto it =
mExtensionBehavior.find(TExtension::ANGLE_base_vertex_base_instance_shader_builtin); if (it != mExtensionBehavior.end())
{
mExtensionBehavior.erase(it);
}
}
// First string is path of source file if flag is set. The actual source follows.
size_t firstSource = 0; if (compileOptions.sourcePath)
{
mSourcePath = shaderStrings[0];
++firstSource;
}
// We preserve symbols at the built-in level from compile-to-compile. // Start pushing the user-defined symbols at global level.
TScopedSymbolTableLevel globalLevel(&mSymbolTable);
ASSERT(mSymbolTable.atGlobalLevel());
ASSERT(parseContext); switch (mShaderType)
{ case GL_COMPUTE_SHADER: if (mShaderVersion < 310)
{
mDiagnostics.globalError("Compute shader is not supported in this shader version."); returnfalse;
} break;
case GL_GEOMETRY_SHADER_EXT: if (mShaderVersion < 310)
{
mDiagnostics.globalError( "Geometry shader is not supported in this shader version."); returnfalse;
} elseif (mShaderVersion == 310)
{ if (!parseContext->checkCanUseOneOfExtensions(
sh::TSourceLoc(),
std::array<TExtension, 2u>{
{TExtension::EXT_geometry_shader, TExtension::OES_geometry_shader}}))
{ returnfalse;
}
} break;
case GL_TESS_CONTROL_SHADER_EXT: case GL_TESS_EVALUATION_SHADER_EXT: if (mShaderVersion < 310)
{
mDiagnostics.globalError( "Tessellation shaders are not supported in this shader version."); returnfalse;
} elseif (mShaderVersion == 310)
{ if (!parseContext->checkCanUseExtension(sh::TSourceLoc(),
TExtension::EXT_tessellation_shader))
{ returnfalse;
}
} break;
#ifdefined(ANGLE_ENABLE_ASSERTS) if (!valid)
{
OutputTree(root, mInfoSink.info);
fprintf(stderr, "AST validation error(s):\n%s\n", mInfoSink.info.c_str());
} #endif // In debug, assert validation. In release, validation errors will be returned back to the // application as internal ANGLE errors.
ASSERT(valid);
// Desktop GLSL shaders don't have precision, so don't expect them to be specified.
mValidateASTOptions.validatePrecision = !IsDesktopGLSpec(mShaderSpec);
if (!validateAST(root))
{ returnfalse;
}
// For now, rewrite pixel local storage before collecting variables or any operations on images. // // TODO(anglebug.com/7279): // Should this actually run after collecting variables? // Do we need more introspection? // Do we want to hide rewritten shader image uniforms from glGetActiveUniform? if (hasPixelLocalStorageUniforms())
{
ASSERT(
IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_shader_pixel_local_storage)); if (!RewritePixelLocalStorage(this, root, getSymbolTable(), compileOptions,
getShaderVersion()))
{
mDiagnostics.globalError("internal compiler error translating pixel local storage"); returnfalse;
}
}
// Disallow expressions deemed too complex. if (compileOptions.limitExpressionComplexity && !limitExpressionComplexity(root))
{ returnfalse;
}
if (shouldRunLoopAndIndexingValidation(compileOptions) &&
!ValidateLimitations(root, mShaderType, &mSymbolTable, &mDiagnostics))
{ returnfalse;
}
if (shouldLimitTypeSizes() && !ValidateTypeSizeLimitations(mResources, root, &mSymbolTable, &mDiagnostics))
{ returnfalse;
}
if (!ValidateFragColorAndFragData(mShaderType, mShaderVersion, mSymbolTable, &mDiagnostics))
{ returnfalse;
}
// Fold expressions that could not be folded before validation that was done as a part of // parsing. if (!FoldExpressions(this, root, &mDiagnostics))
{ returnfalse;
} // Folding should only be able to generate warnings.
ASSERT(mDiagnostics.numErrors() == 0);
// Validate no barrier() after return before prunning it in |PruneNoOps()| below. if (mShaderType == GL_TESS_CONTROL_SHADER && !ValidateBarrierFunctionCall(root, &mDiagnostics))
{ returnfalse;
}
// We prune no-ops to work around driver bugs and to keep AST processing and output simple. // The following kinds of no-ops are pruned: // 1. Empty declarations "int;". // 2. Literal statements: "1.0;". The ESSL output doesn't define a default precision // for float, so float literal statements would end up with no precision which is // invalid ESSL. // 3. Any unreachable statement after a discard, return, break or continue. // After this empty declarations are not allowed in the AST. if (!PruneNoOps(this, root, &mSymbolTable))
{ returnfalse;
}
mValidateASTOptions.validateNoStatementsAfterBranch = true;
// We need to generate globals early if we have non constant initializers enabled bool initializeLocalsAndGlobals =
compileOptions.initializeUninitializedLocals && !IsOutputHLSL(getOutputType()); bool canUseLoopsToInitialize = !compileOptions.dontUseLoopsToInitializeVariables; bool highPrecisionSupported = isHighPrecisionSupported(); bool enableNonConstantInitializers = IsExtensionEnabled(
mExtensionBehavior, TExtension::EXT_shader_non_constant_global_initializers); // forceDeferGlobalInitializers is needed for MSL // to convert a non-const global. For example: // // int someGlobal = 123; // // to // // int someGlobal; // void main() { // someGlobal = 123; // // This is because MSL doesn't allow statically initialized globals. bool forceDeferGlobalInitializers = getOutputType() == SH_MSL_METAL_OUTPUT;
// Create the function DAG and check there is no recursion if (!initCallDag(root))
{ returnfalse;
}
if (compileOptions.limitCallStackDepth && !checkCallDepth())
{ returnfalse;
}
// Checks which functions are used and if "main" exists
mFunctionMetadata.clear();
mFunctionMetadata.resize(mCallDag.size()); if (!tagUsedFunctions())
{ returnfalse;
}
if (!pruneUnusedFunctions(root))
{ returnfalse;
}
if (IsSpecWithFunctionBodyNewScope(mShaderSpec, mShaderVersion))
{ if (!ReplaceShadowingVariables(this, root, &mSymbolTable))
{ returnfalse;
}
}
// anglebug.com/7484: The ESSL spec has a bug with images as function arguments. The recommended // workaround is to inline functions that accept image arguments. if (mShaderVersion >= 310 && !MonomorphizeUnsupportedFunctions( this, root, &mSymbolTable, compileOptions,
UnsupportedFunctionArgsBitSet{UnsupportedFunctionArgs::Image}))
{ returnfalse;
}
// This pass might emit short circuits so keep it before the short circuit unfolding if (compileOptions.rewriteDoWhileLoops)
{ if (!RewriteDoWhile(this, root, &mSymbolTable))
{ returnfalse;
}
}
if (compileOptions.addAndTrueToLoopCondition)
{ if (!AddAndTrueToLoopCondition(this, root))
{ returnfalse;
}
}
if (compileOptions.unfoldShortCircuit)
{ if (!UnfoldShortCircuitAST(this, root))
{ returnfalse;
}
}
if (compileOptions.regenerateStructNames)
{ if (!RegenerateStructNames(this, root, &mSymbolTable))
{ returnfalse;
}
}
if (mShaderType == GL_VERTEX_SHADER &&
IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_multi_draw))
{ if (compileOptions.emulateGLDrawID)
{ if (!EmulateGLDrawID(this, root, &mSymbolTable, &mUniforms,
shouldCollectVariables(compileOptions)))
{ returnfalse;
}
}
}
if (mShaderType == GL_VERTEX_SHADER &&
IsExtensionEnabled(mExtensionBehavior,
TExtension::ANGLE_base_vertex_base_instance_shader_builtin))
{ if (compileOptions.emulateGLBaseVertexBaseInstance)
{ if (!EmulateGLBaseVertexBaseInstance(this, root, &mSymbolTable, &mUniforms,
shouldCollectVariables(compileOptions),
compileOptions.addBaseVertexToVertexID))
{ returnfalse;
}
}
}
int simplifyScalarized = compileOptions.scalarizeVecAndMatConstructorArgs
? IntermNodePatternMatcher::kScalarizedVecOrMatConstructor
: 0;
// Split multi declarations and remove calls to array length(). // Note that SimplifyLoopConditions needs to be run before any other AST transformations // that may need to generate new statements from loop conditions or loop expressions. if (!SimplifyLoopConditions(this, root,
IntermNodePatternMatcher::kMultiDeclaration |
IntermNodePatternMatcher::kArrayLengthMethod |
simplifyScalarized,
&getSymbolTable()))
{ returnfalse;
}
// Note that separate declarations need to be run before other AST transformations that // generate new statements from expressions. if (!SeparateDeclarations(this, root, &getSymbolTable()))
{ returnfalse;
}
mValidateASTOptions.validateMultiDeclarations = true;
if (!SplitSequenceOperator(this, root,
IntermNodePatternMatcher::kArrayLengthMethod | simplifyScalarized,
&getSymbolTable()))
{ returnfalse;
}
if (!RemoveArrayLengthMethod(this, root))
{ returnfalse;
}
if (!RemoveUnreferencedVariables(this, root, &mSymbolTable))
{ returnfalse;
}
// In case the last case inside a switch statement is a certain type of no-op, GLSL compilers in // drivers may not accept it. In this case we clean up the dead code from the end of switch // statements. This is also required because PruneNoOps or RemoveUnreferencedVariables may have // left switch statements that only contained an empty declaration inside the final case in an // invalid state. Relies on that PruneNoOps and RemoveUnreferencedVariables have already been // run. if (!PruneEmptyCases(this, root))
{ returnfalse;
}
// Built-in function emulation needs to happen after validateLimitations pass.
GetGlobalPoolAllocator()->lock();
initBuiltInFunctionEmulator(&mBuiltInFunctionEmulator, compileOptions);
GetGlobalPoolAllocator()->unlock();
mBuiltInFunctionEmulator.markBuiltInFunctionsForEmulation(root);
if (compileOptions.scalarizeVecAndMatConstructorArgs)
{ if (!ScalarizeVecAndMatConstructorArgs(this, root, &mSymbolTable))
{ returnfalse;
}
}
if (compileOptions.forceShaderPrecisionHighpToMediump)
{ if (!ForceShaderPrecisionToMediump(root, &mSymbolTable, mShaderType))
{ returnfalse;
}
}
if (shouldCollectVariables(compileOptions))
{
ASSERT(!mVariablesCollected);
CollectVariables(root, &mAttributes, &mOutputVariables, &mUniforms, &mInputVaryings,
&mOutputVaryings, &mSharedVariables, &mUniformBlocks,
&mShaderStorageBlocks, mResources.HashFunction, &mSymbolTable, mShaderType,
mExtensionBehavior, mResources, mTessControlShaderOutputVertices);
collectInterfaceBlocks();
mVariablesCollected = true; if (compileOptions.useUnusedStandardSharedBlocks)
{ if (!useAllMembersInUnusedStandardAndSharedBlocks(root))
{ returnfalse;
}
} if (compileOptions.enforcePackingRestrictions)
{ int maxUniformVectors = GetMaxUniformVectorsForShaderType(mShaderType, mResources); // Returns true if, after applying the packing rules in the GLSL ES 1.00.17 spec // Appendix A, section 7, the shader does not use too many uniforms. if (!CheckVariablesInPackingLimits(maxUniformVectors, mUniforms))
{
mDiagnostics.globalError("too many uniforms"); returnfalse;
}
} bool needInitializeOutputVariables =
compileOptions.initOutputVariables && mShaderType != GL_COMPUTE_SHADER;
needInitializeOutputVariables |=
compileOptions.initFragmentOutputVariables && mShaderType == GL_FRAGMENT_SHADER; if (needInitializeOutputVariables)
{ if (!initializeOutputVariables(root))
{ returnfalse;
}
}
}
// Removing invariant declarations must be done after collecting variables. // Otherwise, built-in invariant declarations don't apply. if (RemoveInvariant(mShaderType, mShaderVersion, mOutputType, compileOptions))
{ if (!RemoveInvariantDeclaration(this, root))
{ returnfalse;
}
}
// gl_Position is always written in compatibility output mode. // It may have been already initialized among other output variables, in that case we don't // need to initialize it twice. if (mShaderType == GL_VERTEX_SHADER && !mGLPositionInitialized &&
(compileOptions.initGLPosition || mOutputType == SH_GLSL_COMPATIBILITY_OUTPUT))
{ if (!initializeGLPosition(root))
{ returnfalse;
}
mGLPositionInitialized = true;
}
// DeferGlobalInitializers needs to be run before other AST transformations that generate new // statements from expressions. But it's fine to run DeferGlobalInitializers after the above // SplitSequenceOperator and RemoveArrayLengthMethod since they only have an effect on the AST // on ESSL >= 3.00, and the initializers that need to be deferred can only exist in ESSL < 3.00. // Exception: if EXT_shader_non_constant_global_initializers is enabled, we must generate global // initializers before we generate the DAG, since initializers may call functions which must not // be optimized out if (!enableNonConstantInitializers &&
!DeferGlobalInitializers(this, root, initializeLocalsAndGlobals, canUseLoopsToInitialize,
highPrecisionSupported, forceDeferGlobalInitializers,
&mSymbolTable))
{ returnfalse;
}
if (initializeLocalsAndGlobals)
{ // Initialize uninitialized local variables. // In some cases initializing can generate extra statements in the parent block, such as // when initializing nameless structs or initializing arrays in ESSL 1.00. In that case // we need to first simplify loop conditions. We've already separated declarations // earlier, which is also required. If we don't follow the Appendix A limitations, loop // init statements can declare arrays or nameless structs and have multiple // declarations.
if (!shouldRunLoopAndIndexingValidation(compileOptions))
{ if (!SimplifyLoopConditions(this, root,
IntermNodePatternMatcher::kArrayDeclaration |
IntermNodePatternMatcher::kNamelessStructDeclaration,
&getSymbolTable()))
{ returnfalse;
}
}
// Apply key workarounds. if (shouldFlattenPragmaStdglInvariantAll())
{ // This should be harmless to do in all cases, but for the moment, do it only conditionally.
compileOptions.flattenPragmaSTDGLInvariantAll = true;
}
// The IntermNode tree doesn't need to be deleted here, since the // memory will be freed in a big chunk by the PoolAllocator. returntrue;
} returnfalse;
}
switch (mCallDag.init(root, &mDiagnostics))
{ case CallDAG::INITDAG_SUCCESS: returntrue; case CallDAG::INITDAG_RECURSION: case CallDAG::INITDAG_UNDEFINED: // Error message has already been written out.
ASSERT(mDiagnostics.numErrors() > 0); returnfalse;
}
if (depth >= mResources.MaxCallStackDepth)
{ // Trace back the function chain to have a meaningful info log.
std::stringstream errorStream = sh::InitializeStream<std::stringstream>();
errorStream << "Call stack too deep (larger than " << mResources.MaxCallStackDepth
<< ") with the following call chain: "
<< record.node->getFunction()->name();
int currentFunction = static_cast<int>(i); int currentDepth = depth;
bool TCompiler::tagUsedFunctions()
{ // Search from main, starting from the end of the DAG as it usually is the root. for (size_t i = mCallDag.size(); i-- > 0;)
{ if (mCallDag.getRecordFromIndex(i).node->getFunction()->isMain())
{
internalTagUsedFunction(i); returntrue;
}
}
// If a function is unused, it may have a struct declaration in its return value which // shouldn't be pruned. In that case, replace the function definition with the struct // definition.
ASSERT(function != nullptr); const TType &returnType = function->getReturnType(); if (!returnType.isStructSpecifier())
{ continue;
}
TVariable *structVariable = new TVariable(&mSymbolTable, kEmptyImmutableString, &returnType, SymbolType::Empty);
TIntermSymbol *structSymbol = new TIntermSymbol(structVariable);
TIntermDeclaration *structDeclaration = new TIntermDeclaration;
structDeclaration->appendDeclarator(structSymbol);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.