Commit d10cf69e by Olli Etuaho Committed by Commit Bot

Remove repeated "success" check from compileTreeImpl

This refactoring simplifies the compilation code by putting AST checks and simplification into a separate function. This function will immediately return false when an error is encountered. This is easier to maintain than repeated checking of a "success" boolean. BUG=angleproject:2068 TEST=WebGL conformance tests, angle_unittests, angle_end2end_tests Change-Id: I1ae1c8def3625ada1482104a6babe605405229ef Reviewed-on: https://chromium-review.googlesource.com/750085Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent 58f67be0
......@@ -335,275 +335,286 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
TScopedSymbolTableLevel scopedSymbolLevel(&symbolTable);
// Parse shader.
bool success = (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr,
&parseContext) == 0) &&
(parseContext.getTreeRoot() != nullptr);
if (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr,
&parseContext) != 0)
{
return nullptr;
}
shaderVersion = parseContext.getShaderVersion();
if (success && MapSpecToShaderVersion(shaderSpec) < shaderVersion)
if (parseContext.getTreeRoot() == nullptr)
{
mDiagnostics.globalError("unsupported shader version");
success = false;
return nullptr;
}
TIntermBlock *root = nullptr;
setASTMetadata(parseContext);
if (success)
if (MapSpecToShaderVersion(shaderSpec) < shaderVersion)
{
mPragma = parseContext.pragma();
symbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll);
mDiagnostics.globalError("unsupported shader version");
return nullptr;
}
mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared();
mComputeShaderLocalSize = parseContext.getComputeShaderLocalSize();
TIntermBlock *root = parseContext.getTreeRoot();
if (!checkAndSimplifyAST(root, parseContext, compileOptions))
{
return nullptr;
}
mNumViews = parseContext.getNumViews();
return root;
}
root = parseContext.getTreeRoot();
void TCompiler::setASTMetadata(const TParseContext &parseContext)
{
shaderVersion = parseContext.getShaderVersion();
// Highp might have been auto-enabled based on shader version
fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh();
mPragma = parseContext.pragma();
symbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll);
if (success && shaderType == GL_GEOMETRY_SHADER_OES)
{
mGeometryShaderInputPrimitiveType = parseContext.getGeometryShaderInputPrimitiveType();
mGeometryShaderOutputPrimitiveType =
parseContext.getGeometryShaderOutputPrimitiveType();
mGeometryShaderMaxVertices = parseContext.getGeometryShaderMaxVertices();
mGeometryShaderInvocations = parseContext.getGeometryShaderInvocations();
}
mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared();
mComputeShaderLocalSize = parseContext.getComputeShaderLocalSize();
// Disallow expressions deemed too complex.
if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY))
success = limitExpressionComplexity(root);
// 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.
// After this empty declarations are not allowed in the AST.
if (success)
PruneNoOps(root);
// 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 may have left switch
// statements that only contained an empty declaration inside the final case in an invalid
// state. Relies on that PruneNoOps has already been run.
if (success)
RemoveNoOpCasesFromEndOfSwitchStatements(root, &symbolTable);
// Remove empty switch statements - this makes output simpler.
if (success)
RemoveEmptySwitchStatements(root);
// Create the function DAG and check there is no recursion
if (success)
success = initCallDag(root);
if (success && (compileOptions & SH_LIMIT_CALL_STACK_DEPTH))
success = checkCallDepth();
// Checks which functions are used and if "main" exists
if (success)
{
functionMetadata.clear();
functionMetadata.resize(mCallDag.size());
success = tagUsedFunctions();
}
mNumViews = parseContext.getNumViews();
if (success && !(compileOptions & SH_DONT_PRUNE_UNUSED_FUNCTIONS))
success = pruneUnusedFunctions(root);
// Highp might have been auto-enabled based on shader version
fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh();
if (success && shaderVersion >= 310)
{
success = ValidateVaryingLocations(root, &mDiagnostics, shaderType);
}
if (shaderType == GL_GEOMETRY_SHADER_OES)
{
mGeometryShaderInputPrimitiveType = parseContext.getGeometryShaderInputPrimitiveType();
mGeometryShaderOutputPrimitiveType = parseContext.getGeometryShaderOutputPrimitiveType();
mGeometryShaderMaxVertices = parseContext.getGeometryShaderMaxVertices();
mGeometryShaderInvocations = parseContext.getGeometryShaderInvocations();
}
}
if (success && shaderVersion >= 300 && shaderType == GL_FRAGMENT_SHADER)
{
success = ValidateOutputs(root, getExtensionBehavior(), compileResources.MaxDrawBuffers,
&mDiagnostics);
}
bool TCompiler::checkAndSimplifyAST(TIntermBlock *root,
const TParseContext &parseContext,
ShCompileOptions compileOptions)
{
// Disallow expressions deemed too complex.
if ((compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY) && !limitExpressionComplexity(root))
{
return false;
}
if (success && shouldRunLoopAndIndexingValidation(compileOptions))
success =
ValidateLimitations(root, shaderType, &symbolTable, shaderVersion, &mDiagnostics);
// 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.
// After this empty declarations are not allowed in the AST.
PruneNoOps(root);
// 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 may have left switch
// statements that only contained an empty declaration inside the final case in an invalid
// state. Relies on that PruneNoOps has already been run.
RemoveNoOpCasesFromEndOfSwitchStatements(root, &symbolTable);
// Remove empty switch statements - this makes output simpler.
RemoveEmptySwitchStatements(root);
// Create the function DAG and check there is no recursion
if (!initCallDag(root))
{
return false;
}
// Fail compilation if precision emulation not supported.
if (success && getResources().WEBGL_debug_shader_precision &&
getPragma().debugShaderPrecision)
{
if (!EmulatePrecision::SupportedInLanguage(outputType))
{
mDiagnostics.globalError("Precision emulation not supported for this output type.");
success = false;
}
}
if ((compileOptions & SH_LIMIT_CALL_STACK_DEPTH) && !checkCallDepth())
{
return false;
}
// Built-in function emulation needs to happen after validateLimitations pass.
if (success)
{
// TODO(jmadill): Remove global pool allocator.
GetGlobalPoolAllocator()->lock();
initBuiltInFunctionEmulator(&builtInFunctionEmulator, compileOptions);
GetGlobalPoolAllocator()->unlock();
builtInFunctionEmulator.markBuiltInFunctionsForEmulation(root);
}
// Checks which functions are used and if "main" exists
functionMetadata.clear();
functionMetadata.resize(mCallDag.size());
if (!tagUsedFunctions())
{
return false;
}
// Clamping uniform array bounds needs to happen after validateLimitations pass.
if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
if (!(compileOptions & SH_DONT_PRUNE_UNUSED_FUNCTIONS))
{
pruneUnusedFunctions(root);
}
if (success && (compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) &&
parseContext.isExtensionEnabled(TExtension::OVR_multiview) &&
getShaderType() != GL_COMPUTE_SHADER)
{
DeclareAndInitBuiltinsForInstancedMultiview(root, mNumViews, shaderType, compileOptions,
outputType, &symbolTable);
}
if (shaderVersion >= 310 && !ValidateVaryingLocations(root, &mDiagnostics, shaderType))
{
return false;
}
// This pass might emit short circuits so keep it before the short circuit unfolding
if (success && (compileOptions & SH_REWRITE_DO_WHILE_LOOPS))
RewriteDoWhile(root, &symbolTable);
if (shaderVersion >= 300 && shaderType == GL_FRAGMENT_SHADER &&
!ValidateOutputs(root, getExtensionBehavior(), compileResources.MaxDrawBuffers,
&mDiagnostics))
{
return false;
}
if (success && (compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION))
sh::AddAndTrueToLoopCondition(root);
if (shouldRunLoopAndIndexingValidation(compileOptions) &&
!ValidateLimitations(root, shaderType, &symbolTable, shaderVersion, &mDiagnostics))
{
return false;
}
if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT))
{
UnfoldShortCircuitAST unfoldShortCircuit;
root->traverse(&unfoldShortCircuit);
unfoldShortCircuit.updateTree();
}
// Fail compilation if precision emulation not supported.
if (getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision &&
!EmulatePrecision::SupportedInLanguage(outputType))
{
mDiagnostics.globalError("Precision emulation not supported for this output type.");
return false;
}
if (success && (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT))
// Built-in function emulation needs to happen after validateLimitations pass.
// TODO(jmadill): Remove global pool allocator.
GetGlobalPoolAllocator()->lock();
initBuiltInFunctionEmulator(&builtInFunctionEmulator, compileOptions);
GetGlobalPoolAllocator()->unlock();
builtInFunctionEmulator.markBuiltInFunctionsForEmulation(root);
// Clamping uniform array bounds needs to happen after validateLimitations pass.
if (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS)
{
arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
}
if ((compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) &&
parseContext.isExtensionEnabled(TExtension::OVR_multiview) &&
getShaderType() != GL_COMPUTE_SHADER)
{
DeclareAndInitBuiltinsForInstancedMultiview(root, mNumViews, shaderType, compileOptions,
outputType, &symbolTable);
}
// This pass might emit short circuits so keep it before the short circuit unfolding
if (compileOptions & SH_REWRITE_DO_WHILE_LOOPS)
RewriteDoWhile(root, &symbolTable);
if (compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION)
sh::AddAndTrueToLoopCondition(root);
if (compileOptions & SH_UNFOLD_SHORT_CIRCUIT)
{
UnfoldShortCircuitAST unfoldShortCircuit;
root->traverse(&unfoldShortCircuit);
unfoldShortCircuit.updateTree();
}
if (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT)
{
RemovePow(root);
}
if (shouldCollectVariables(compileOptions))
{
ASSERT(!variablesCollected);
CollectVariables(root, &attributes, &outputVariables, &uniforms, &inputVaryings,
&outputVaryings, &uniformBlocks, &shaderStorageBlocks, &inBlocks,
hashFunction, &symbolTable, shaderVersion, shaderType, extensionBehavior);
collectInterfaceBlocks();
variablesCollected = true;
if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS)
{
RemovePow(root);
useAllMembersInUnusedStandardAndSharedBlocks(root);
}
if (success && shouldCollectVariables(compileOptions))
if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS)
{
ASSERT(!variablesCollected);
CollectVariables(root, &attributes, &outputVariables, &uniforms, &inputVaryings,
&outputVaryings, &uniformBlocks, &shaderStorageBlocks, &inBlocks,
hashFunction, &symbolTable, shaderVersion, shaderType,
extensionBehavior);
collectInterfaceBlocks();
variablesCollected = true;
if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS)
{
useAllMembersInUnusedStandardAndSharedBlocks(root);
}
if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS)
{
// 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.
success = CheckVariablesInPackingLimits(maxUniformVectors, uniforms);
if (!success)
{
mDiagnostics.globalError("too many uniforms");
}
}
if (success && (compileOptions & SH_INIT_OUTPUT_VARIABLES))
// 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, uniforms))
{
initializeOutputVariables(root);
mDiagnostics.globalError("too many uniforms");
return false;
}
}
// 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 (success && shaderType == GL_VERTEX_SHADER && !mGLPositionInitialized &&
((compileOptions & SH_INIT_GL_POSITION) ||
(outputType == SH_GLSL_COMPATIBILITY_OUTPUT)))
if (compileOptions & SH_INIT_OUTPUT_VARIABLES)
{
initializeGLPosition(root);
mGLPositionInitialized = true;
initializeOutputVariables(root);
}
}
// Removing invariant declarations must be done after collecting variables.
// Otherwise, built-in invariant declarations don't apply.
if (success && RemoveInvariant(shaderType, shaderVersion, outputType, compileOptions))
sh::RemoveInvariantDeclaration(root);
// 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 (shaderType == GL_VERTEX_SHADER && !mGLPositionInitialized &&
((compileOptions & SH_INIT_GL_POSITION) || (outputType == SH_GLSL_COMPATIBILITY_OUTPUT)))
{
initializeGLPosition(root);
mGLPositionInitialized = true;
}
if (success && (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS))
{
ScalarizeVecAndMatConstructorArgs(root, shaderType, fragmentPrecisionHigh,
&symbolTable);
}
// Removing invariant declarations must be done after collecting variables.
// Otherwise, built-in invariant declarations don't apply.
if (RemoveInvariant(shaderType, shaderVersion, outputType, compileOptions))
{
sh::RemoveInvariantDeclaration(root);
}
if (success && (compileOptions & SH_REGENERATE_STRUCT_NAMES))
{
RegenerateStructNames gen(&symbolTable, shaderVersion);
root->traverse(&gen);
}
if (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS)
{
ScalarizeVecAndMatConstructorArgs(root, shaderType, fragmentPrecisionHigh, &symbolTable);
}
if (success && shaderType == GL_FRAGMENT_SHADER && shaderVersion == 100 &&
compileResources.EXT_draw_buffers && compileResources.MaxDrawBuffers > 1 &&
IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers))
{
EmulateGLFragColorBroadcast(root, compileResources.MaxDrawBuffers, &outputVariables,
&symbolTable, shaderVersion);
}
if (compileOptions & SH_REGENERATE_STRUCT_NAMES)
{
RegenerateStructNames gen(&symbolTable, shaderVersion);
root->traverse(&gen);
}
if (success)
{
DeferGlobalInitializers(root, needToInitializeGlobalsInAST(), &symbolTable);
}
if (shaderType == GL_FRAGMENT_SHADER && shaderVersion == 100 &&
compileResources.EXT_draw_buffers && compileResources.MaxDrawBuffers > 1 &&
IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers))
{
EmulateGLFragColorBroadcast(root, compileResources.MaxDrawBuffers, &outputVariables,
&symbolTable, shaderVersion);
}
// Split multi declarations and remove calls to array length().
if (success)
{
// 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.
SimplifyLoopConditions(root,
IntermNodePatternMatcher::kMultiDeclaration |
IntermNodePatternMatcher::kArrayLengthMethod,
&getSymbolTable(), getShaderVersion());
DeferGlobalInitializers(root, needToInitializeGlobalsInAST(), &symbolTable);
// Note that separate declarations need to be run before other AST transformations that
// generate new statements from expressions.
SeparateDeclarations(root);
// 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.
SimplifyLoopConditions(
root,
IntermNodePatternMatcher::kMultiDeclaration | IntermNodePatternMatcher::kArrayLengthMethod,
&getSymbolTable(), getShaderVersion());
SplitSequenceOperator(root, IntermNodePatternMatcher::kArrayLengthMethod,
&getSymbolTable(), getShaderVersion());
// Note that separate declarations need to be run before other AST transformations that
// generate new statements from expressions.
SeparateDeclarations(root);
RemoveArrayLengthMethod(root);
}
SplitSequenceOperator(root, IntermNodePatternMatcher::kArrayLengthMethod, &getSymbolTable(),
getShaderVersion());
if (success && (compileOptions & SH_INITIALIZE_UNINITIALIZED_LOCALS) && getOutputType())
{
// 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))
{
SimplifyLoopConditions(root,
IntermNodePatternMatcher::kArrayDeclaration |
IntermNodePatternMatcher::kNamelessStructDeclaration,
&getSymbolTable(), getShaderVersion());
}
InitializeUninitializedLocals(root, getShaderVersion());
}
RemoveArrayLengthMethod(root);
if (success && getShaderType() == GL_VERTEX_SHADER &&
(compileOptions & SH_CLAMP_POINT_SIZE))
if ((compileOptions & SH_INITIALIZE_UNINITIALIZED_LOCALS) && getOutputType())
{
// 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))
{
ClampPointSize(root, compileResources.MaxPointSize, &getSymbolTable());
SimplifyLoopConditions(root,
IntermNodePatternMatcher::kArrayDeclaration |
IntermNodePatternMatcher::kNamelessStructDeclaration,
&getSymbolTable(), getShaderVersion());
}
InitializeUninitializedLocals(root, getShaderVersion());
}
if (success)
return root;
if (getShaderType() == GL_VERTEX_SHADER && (compileOptions & SH_CLAMP_POINT_SIZE))
{
ClampPointSize(root, compileResources.MaxPointSize, &getSymbolTable());
}
return nullptr;
return true;
}
bool TCompiler::compile(const char *const shaderStrings[],
......@@ -985,7 +996,7 @@ class TCompiler::UnusedPredicate
const std::vector<FunctionMetadata> *mMetadatas;
};
bool TCompiler::pruneUnusedFunctions(TIntermBlock *root)
void TCompiler::pruneUnusedFunctions(TIntermBlock *root)
{
UnusedPredicate isUnused(&mCallDag, &functionMetadata);
TIntermSequence *sequence = root->getSequence();
......@@ -995,8 +1006,6 @@ bool TCompiler::pruneUnusedFunctions(TIntermBlock *root)
sequence->erase(std::remove_if(sequence->begin(), sequence->end(), isUnused),
sequence->end());
}
return true;
}
bool TCompiler::limitExpressionComplexity(TIntermBlock *root)
......
......@@ -30,6 +30,7 @@ namespace sh
{
class TCompiler;
class TParseContext;
#ifdef ANGLE_ENABLE_HLSL
class TranslatorHLSL;
#endif // ANGLE_ENABLE_HLSL
......@@ -218,12 +219,21 @@ class TCompiler : public TShHandleBase
// Removes unused function declarations and prototypes from the AST
class UnusedPredicate;
bool pruneUnusedFunctions(TIntermBlock *root);
void pruneUnusedFunctions(TIntermBlock *root);
TIntermBlock *compileTreeImpl(const char *const shaderStrings[],
size_t numStrings,
const ShCompileOptions compileOptions);
// Fetches and stores shader metadata that is not stored within the AST itself, such as shader
// version.
void setASTMetadata(const TParseContext &parseContext);
// Does checks that need to be run after parsing is complete and returns true if they pass.
bool checkAndSimplifyAST(TIntermBlock *root,
const TParseContext &parseContext,
ShCompileOptions compileOptions);
sh::GLenum shaderType;
ShShaderSpec shaderSpec;
ShShaderOutput outputType;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment