author | Ethan Lin <ethlin@mozilla.com> |
Tue, 25 Oct 2016 19:29:00 -0400 | |
changeset 319902 | 57c5a60badc7073ec99c09b1e6468fde23a87b15 |
parent 319901 | c1ef258c04f1960f6c014394f371fc0d3068ccb8 |
child 319903 | 7d22628f37cf9269c288875693c788e7b5b81bc4 |
push id | 30882 |
push user | ryanvm@gmail.com |
push date | Sat, 29 Oct 2016 13:12:06 +0000 |
treeherder | mozilla-central@16cdd6273c48 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jgilbert |
bugs | 1303443 |
milestone | 52.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/canvas/WebGLShaderValidator.cpp +++ b/dom/canvas/WebGLShaderValidator.cpp @@ -29,17 +29,16 @@ IdentifierHashFunc(const char* name, siz } static int ChooseValidatorCompileOptions(const ShBuiltInResources& resources, const mozilla::gl::GLContext* gl) { int options = SH_VARIABLES | SH_ENFORCE_PACKING_RESTRICTIONS | - SH_INIT_VARYINGS_WITHOUT_STATIC_USE | SH_OBJECT_CODE | SH_LIMIT_CALL_STACK_DEPTH | SH_INIT_GL_POSITION; if (resources.MaxExpressionComplexity > 0) { options |= SH_LIMIT_EXPRESSION_COMPLEXITY; } @@ -49,48 +48,39 @@ ChooseValidatorCompileOptions(const ShBu // Just do it universally. options |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX; if (gfxPrefs::WebGLAllANGLEOptions()) { return options | SH_VALIDATE_LOOP_INDEXING | SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX | SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX | - SH_EMULATE_BUILT_IN_FUNCTIONS | SH_CLAMP_INDIRECT_ARRAY_BOUNDS | SH_UNFOLD_SHORT_CIRCUIT | SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS | + SH_INIT_OUTPUT_VARIABLES | SH_REGENERATE_STRUCT_NAMES; } #ifndef XP_MACOSX // We want to do this everywhere, but to do this on Mac, we need // to do it only on Mac OSX > 10.6 as this causes the shader // compiler in 10.6 to crash options |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS; #endif #ifdef XP_MACOSX if (gl->WorkAroundDriverBugs()) { // Work around https://bugs.webkit.org/show_bug.cgi?id=124684, // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb options |= SH_UNFOLD_SHORT_CIRCUIT; - // Work around bug 665578 and bug 769810 - if (gl->Vendor() == gl::GLVendor::ATI) { - options |= SH_EMULATE_BUILT_IN_FUNCTIONS; - } - - // Work around bug 735560 - if (gl->Vendor() == gl::GLVendor::Intel) { - options |= SH_EMULATE_BUILT_IN_FUNCTIONS; - } - // Work around that Mac drivers handle struct scopes incorrectly. options |= SH_REGENERATE_STRUCT_NAMES; + options |= SH_INIT_OUTPUT_VARIABLES; } #endif return options; } } // namespace webgl
--- a/gfx/angle/BUILD.gn +++ b/gfx/angle/BUILD.gn @@ -492,33 +492,37 @@ util_gypi = exec_script("//build/gypi_to config("angle_util_config") { include_dirs = [ "util" ] if (is_linux && use_x11) { libs = [ "X11" ] } } -static_library("angle_util") { +shared_library("angle_util") { sources = rebase_path(util_gypi.util_sources, ".", "util") if (is_win) { sources += rebase_path(util_gypi.util_win32_sources, ".", "util") } if (is_linux) { sources += rebase_path(util_gypi.util_linux_sources, ".", "util") libs = [ "rt", "dl", ] } if (is_mac) { sources += rebase_path(util_gypi.util_osx_sources, ".", "util") + libs = [ + "AppKit.framework", + "QuartzCore.framework", + ] } if (use_x11) { sources += rebase_path(util_gypi.util_x11_sources, ".", "util") } if (is_android) { # To prevent linux sources filtering on android @@ -533,26 +537,34 @@ static_library("angle_util") { if (use_ozone) { sources += rebase_path(util_gypi.util_ozone_sources, ".", "util") } defines = [ "GL_GLEXT_PROTOTYPES", "EGL_EGLEXT_PROTOTYPES", + "LIBANGLE_UTIL_IMPLEMENTATION", ] configs += [ ":debug_annotations_config", ":extra_warnings", ] public_configs = [ ":angle_util_config", ":internal_config", ] + if (is_mac && !is_component_build) { + ldflags = [ + "-install_name", + "@rpath/lib${target_name}.dylib", + ] + public_configs += [ ":shared_library_public_config" ] + } deps = [ ":angle_common", ":libEGL", ":libGLESv2", ] }
--- a/gfx/angle/include/GLSLANG/ShaderLang.h +++ b/gfx/angle/include/GLSLANG/ShaderLang.h @@ -44,49 +44,28 @@ typedef unsigned int GLenum; } // Must be included after GLenum proxy typedef // Note: make sure to increment ANGLE_SH_VERSION when changing ShaderVars.h #include "ShaderVars.h" // Version number for shader translation API. // It is incremented every time the API changes. -#define ANGLE_SH_VERSION 155 +#define ANGLE_SH_VERSION 161 typedef enum { SH_GLES2_SPEC, SH_WEBGL_SPEC, SH_GLES3_SPEC, SH_WEBGL2_SPEC, SH_GLES3_1_SPEC, SH_WEBGL3_SPEC, - // The CSS Shaders spec is a subset of the WebGL spec. - // - // In both CSS vertex and fragment shaders, ANGLE: - // (1) Reserves the "css_" prefix. - // (2) Renames the main function to css_main. - // (3) Disables the gl_MaxDrawBuffers built-in. - // - // In CSS fragment shaders, ANGLE: - // (1) Disables the gl_FragColor built-in. - // (2) Disables the gl_FragData built-in. - // (3) Enables the css_MixColor built-in. - // (4) Enables the css_ColorMatrix built-in. - // - // After passing a CSS shader through ANGLE, the browser is expected to append - // a new main function to it. - // This new main function will call the css_main function. - // It may also perform additional operations like varying assignment, texture - // access, and gl_FragColor assignment in order to implement the CSS Shaders - // blend modes. - // - SH_CSS_SHADERS_SPEC } ShShaderSpec; typedef enum { // ESSL output only supported in some configurations. SH_ESSL_OUTPUT = 0x8B45, // GLSL output only supported in some configurations. @@ -111,132 +90,119 @@ typedef enum // Prefer using these to specify HLSL output type: SH_HLSL_3_0_OUTPUT = 0x8B48, // D3D 9 SH_HLSL_4_1_OUTPUT = 0x8B49, // D3D 11 SH_HLSL_4_0_FL9_3_OUTPUT = 0x8B4A // D3D 11 feature level 9_3 } ShShaderOutput; // Compile options. -typedef enum { - SH_VALIDATE = 0, - SH_VALIDATE_LOOP_INDEXING = 0x0001, - SH_INTERMEDIATE_TREE = 0x0002, - SH_OBJECT_CODE = 0x0004, - SH_VARIABLES = 0x0008, - SH_LINE_DIRECTIVES = 0x0010, - SH_SOURCE_PATH = 0x0020, - SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX = 0x0040, - // If a sampler array index happens to be a loop index, - // 1) if its type is integer, unroll the loop. - // 2) if its type is float, fail the shader compile. - // This is to work around a mac driver bug. - SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX = 0x0080, +typedef uint64_t ShCompileOptions; - // This is needed only as a workaround for certain OpenGL driver bugs. - SH_EMULATE_BUILT_IN_FUNCTIONS = 0x0100, +const ShCompileOptions SH_VALIDATE = 0; +const ShCompileOptions SH_VALIDATE_LOOP_INDEXING = UINT64_C(1) << 0; +const ShCompileOptions SH_INTERMEDIATE_TREE = UINT64_C(1) << 1; +const ShCompileOptions SH_OBJECT_CODE = UINT64_C(1) << 2; +const ShCompileOptions SH_VARIABLES = UINT64_C(1) << 3; +const ShCompileOptions SH_LINE_DIRECTIVES = UINT64_C(1) << 4; +const ShCompileOptions SH_SOURCE_PATH = UINT64_C(1) << 5; +const ShCompileOptions SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX = UINT64_C(1) << 6; +// If a sampler array index happens to be a loop index, +// 1) if its type is integer, unroll the loop. +// 2) if its type is float, fail the shader compile. +// This is to work around a mac driver bug. +const ShCompileOptions SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX = UINT64_C(1) << 7; - // This is an experimental flag to enforce restrictions that aim to prevent - // timing attacks. - // It generates compilation errors for shaders that could expose sensitive - // texture information via the timing channel. - // To use this flag, you must compile the shader under the WebGL spec - // (using the SH_WEBGL_SPEC flag). - SH_TIMING_RESTRICTIONS = 0x0200, +// This flag works around bug in Intel Mac drivers related to abs(i) where +// i is an integer. +const ShCompileOptions SH_EMULATE_ABS_INT_FUNCTION = UINT64_C(1) << 8; - // This flag prints the dependency graph that is used to enforce timing - // restrictions on fragment shaders. - // This flag only has an effect if all of the following are true: - // - The shader spec is SH_WEBGL_SPEC. - // - The compile options contain the SH_TIMING_RESTRICTIONS flag. - // - The shader type is GL_FRAGMENT_SHADER. - SH_DEPENDENCY_GRAPH = 0x0400, +// Enforce the GLSL 1.017 Appendix A section 7 packing restrictions. +// This flag only enforces (and can only enforce) the packing +// restrictions for uniform variables in both vertex and fragment +// shaders. ShCheckVariablesWithinPackingLimits() lets embedders +// enforce the packing restrictions for varying variables during +// program link time. +const ShCompileOptions SH_ENFORCE_PACKING_RESTRICTIONS = UINT64_C(1) << 9; - // Enforce the GLSL 1.017 Appendix A section 7 packing restrictions. - // This flag only enforces (and can only enforce) the packing - // restrictions for uniform variables in both vertex and fragment - // shaders. ShCheckVariablesWithinPackingLimits() lets embedders - // enforce the packing restrictions for varying variables during - // program link time. - SH_ENFORCE_PACKING_RESTRICTIONS = 0x0800, +// This flag ensures all indirect (expression-based) array indexing +// is clamped to the bounds of the array. This ensures, for example, +// that you cannot read off the end of a uniform, whether an array +// vec234, or mat234 type. The ShArrayIndexClampingStrategy enum, +// specified in the ShBuiltInResources when constructing the +// compiler, selects the strategy for the clamping implementation. +const ShCompileOptions SH_CLAMP_INDIRECT_ARRAY_BOUNDS = UINT64_C(1) << 10; - // This flag ensures all indirect (expression-based) array indexing - // is clamped to the bounds of the array. This ensures, for example, - // that you cannot read off the end of a uniform, whether an array - // vec234, or mat234 type. The ShArrayIndexClampingStrategy enum, - // specified in the ShBuiltInResources when constructing the - // compiler, selects the strategy for the clamping implementation. - SH_CLAMP_INDIRECT_ARRAY_BOUNDS = 0x1000, +// This flag limits the complexity of an expression. +const ShCompileOptions SH_LIMIT_EXPRESSION_COMPLEXITY = UINT64_C(1) << 11; + +// This flag limits the depth of the call stack. +const ShCompileOptions SH_LIMIT_CALL_STACK_DEPTH = UINT64_C(1) << 12; - // This flag limits the complexity of an expression. - SH_LIMIT_EXPRESSION_COMPLEXITY = 0x2000, - - // This flag limits the depth of the call stack. - SH_LIMIT_CALL_STACK_DEPTH = 0x4000, +// This flag initializes gl_Position to vec4(0,0,0,0) at the +// beginning of the vertex shader's main(), and has no effect in the +// fragment shader. It is intended as a workaround for drivers which +// incorrectly fail to link programs if gl_Position is not written. +const ShCompileOptions SH_INIT_GL_POSITION = UINT64_C(1) << 13; - // This flag initializes gl_Position to vec4(0,0,0,0) at the - // beginning of the vertex shader's main(), and has no effect in the - // fragment shader. It is intended as a workaround for drivers which - // incorrectly fail to link programs if gl_Position is not written. - SH_INIT_GL_POSITION = 0x8000, +// This flag replaces +// "a && b" with "a ? b : false", +// "a || b" with "a ? true : b". +// This is to work around a MacOSX driver bug that |b| is executed +// independent of |a|'s value. +const ShCompileOptions SH_UNFOLD_SHORT_CIRCUIT = UINT64_C(1) << 14; - // This flag replaces - // "a && b" with "a ? b : false", - // "a || b" with "a ? true : b". - // This is to work around a MacOSX driver bug that |b| is executed - // independent of |a|'s value. - SH_UNFOLD_SHORT_CIRCUIT = 0x10000, +// This flag initializes output variables to 0 at the beginning of main(). +// It is to avoid undefined behaviors. +const ShCompileOptions SH_INIT_OUTPUT_VARIABLES = UINT64_C(1) << 15; - // This flag initializes output variables to 0 at the beginning of main(). - // It is to avoid undefined behaviors. - SH_INIT_OUTPUT_VARIABLES = 0x20000, - // TODO(zmo): obsolete, remove after ANGLE roll into Chromium. - SH_INIT_VARYINGS_WITHOUT_STATIC_USE = 0x20000, +// This flag scalarizes vec/ivec/bvec/mat constructor args. +// It is intended as a workaround for Linux/Mac driver bugs. +const ShCompileOptions SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS = UINT64_C(1) << 16; - // This flag scalarizes vec/ivec/bvec/mat constructor args. - // It is intended as a workaround for Linux/Mac driver bugs. - SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS = 0x40000, +// This flag overwrites a struct name with a unique prefix. +// It is intended as a workaround for drivers that do not handle +// struct scopes correctly, including all Mac drivers and Linux AMD. +const ShCompileOptions SH_REGENERATE_STRUCT_NAMES = UINT64_C(1) << 17; - // This flag overwrites a struct name with a unique prefix. - // It is intended as a workaround for drivers that do not handle - // struct scopes correctly, including all Mac drivers and Linux AMD. - SH_REGENERATE_STRUCT_NAMES = 0x80000, +// This flag makes the compiler not prune unused function early in the +// compilation process. Pruning coupled with SH_LIMIT_CALL_STACK_DEPTH +// helps avoid bad shaders causing stack overflows. +const ShCompileOptions SH_DONT_PRUNE_UNUSED_FUNCTIONS = UINT64_C(1) << 18; - // This flag makes the compiler not prune unused function early in the - // compilation process. Pruning coupled with SH_LIMIT_CALL_STACK_DEPTH - // helps avoid bad shaders causing stack overflows. - SH_DONT_PRUNE_UNUSED_FUNCTIONS = 0x100000, +// This flag works around a bug in NVIDIA 331 series drivers related +// to pow(x, y) where y is a constant vector. +const ShCompileOptions SH_REMOVE_POW_WITH_CONSTANT_EXPONENT = UINT64_C(1) << 19; - // This flag works around a bug in NVIDIA 331 series drivers related - // to pow(x, y) where y is a constant vector. - SH_REMOVE_POW_WITH_CONSTANT_EXPONENT = 0x200000, +// This flag works around bugs in Mac drivers related to do-while by +// transforming them into an other construct. +const ShCompileOptions SH_REWRITE_DO_WHILE_LOOPS = UINT64_C(1) << 20; - // This flag works around bugs in Mac drivers related to do-while by - // transforming them into an other construct. - SH_REWRITE_DO_WHILE_LOOPS = 0x400000, +// This flag works around a bug in the HLSL compiler optimizer that folds certain +// constant pow expressions incorrectly. Only applies to the HLSL back-end. It works +// by expanding the integer pow expressions into a series of multiplies. +const ShCompileOptions SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS = UINT64_C(1) << 21; - // This flag works around a bug in the HLSL compiler optimizer that folds certain - // constant pow expressions incorrectly. Only applies to the HLSL back-end. It works - // by expanding the integer pow expressions into a series of multiplies. - SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS = 0x800000, - - // Flatten "#pragma STDGL invariant(all)" into the declarations of - // varying variables and built-in GLSL variables. This compiler - // option is enabled automatically when needed. - SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL = 0x1000000, +// Flatten "#pragma STDGL invariant(all)" into the declarations of +// varying variables and built-in GLSL variables. This compiler +// option is enabled automatically when needed. +const ShCompileOptions SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL = UINT64_C(1) << 22; - // Some drivers do not take into account the base level of the texture in the results of the - // HLSL GetDimensions builtin. This flag instructs the compiler to manually add the base level - // offsetting. - SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL = 0x2000000, +// Some drivers do not take into account the base level of the texture in the results of the +// HLSL GetDimensions builtin. This flag instructs the compiler to manually add the base level +// offsetting. +const ShCompileOptions SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL = UINT64_C(1) << 23; - // This flag works around an issue in translating GLSL function texelFetchOffset on - // INTEL drivers. It works by translating texelFetchOffset into texelFetch. - SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH = 0x4000000, -} ShCompileOptions; +// This flag works around an issue in translating GLSL function texelFetchOffset on +// INTEL drivers. It works by translating texelFetchOffset into texelFetch. +const ShCompileOptions SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH = UINT64_C(1) << 24; + +// This flag works around condition bug of for and while loops in Intel Mac OSX drivers. +// Condition calculation is not correct. Rewrite it from "CONDITION" to "CONDITION && true". +const ShCompileOptions SH_ADD_AND_TRUE_TO_LOOP_CONDITION = UINT64_C(1) << 25; // Defines alternate strategies for implementing array index clamping. typedef enum { // Use the clamp intrinsic for array index clamping. SH_CLAMP_WITH_CLAMP_INTRINSIC = 1, // Use a user-defined function for array index clamping. SH_CLAMP_WITH_USER_DEFINED_INT_CLAMP_FUNCTION @@ -460,21 +426,20 @@ COMPILER_EXPORT void ShDestruct(ShHandle // compiling for WebGL - it is implied. // SH_INTERMEDIATE_TREE: Writes intermediate tree to info log. // Can be queried by calling ShGetInfoLog(). // SH_OBJECT_CODE: Translates intermediate tree to glsl or hlsl shader. // Can be queried by calling ShGetObjectCode(). // SH_VARIABLES: Extracts attributes, uniforms, and varyings. // Can be queried by calling ShGetVariableInfo(). // -COMPILER_EXPORT bool ShCompile( - const ShHandle handle, - const char * const shaderStrings[], - size_t numStrings, - int compileOptions); +COMPILER_EXPORT bool ShCompile(const ShHandle handle, + const char *const shaderStrings[], + size_t numStrings, + ShCompileOptions compileOptions); // Clears the results from the previous compilation. COMPILER_EXPORT void ShClearResults(const ShHandle handle); // Return the version of the shader language. COMPILER_EXPORT int ShGetShaderVersion(const ShHandle handle); // Return the currently set language output type.
--- a/gfx/angle/include/export.h +++ b/gfx/angle/include/export.h @@ -5,23 +5,25 @@ // // export.h : Defines ANGLE_EXPORT, a macro for exporting functions from the DLL #ifndef LIBGLESV2_EXPORT_H_ #define LIBGLESV2_EXPORT_H_ #if defined(_WIN32) -# if defined(LIBGLESV2_IMPLEMENTATION) || defined(LIBANGLE_IMPLEMENTATION) +#if defined(LIBGLESV2_IMPLEMENTATION) || defined(LIBANGLE_IMPLEMENTATION) || \ + defined(LIBANGLE_UTIL_IMPLEMENTATION) # define ANGLE_EXPORT __declspec(dllexport) # else # define ANGLE_EXPORT __declspec(dllimport) # endif #elif defined(__GNUC__) -# if defined(LIBGLESV2_IMPLEMENTATION) || defined(LIBANGLE_IMPLEMENTATION) +#if defined(LIBGLESV2_IMPLEMENTATION) || defined(LIBANGLE_IMPLEMENTATION) || \ + defined(LIBANGLE_UTIL_IMPLEMENTATION) # define ANGLE_EXPORT __attribute__((visibility ("default"))) # else # define ANGLE_EXPORT # endif #else # define ANGLE_EXPORT #endif
--- a/gfx/angle/moz.build +++ b/gfx/angle/moz.build @@ -23,33 +23,32 @@ UNIFIED_SOURCES += [ 'src/compiler/preprocessor/ExpressionParser.cpp', 'src/compiler/preprocessor/Input.cpp', 'src/compiler/preprocessor/Lexer.cpp', 'src/compiler/preprocessor/Macro.cpp', 'src/compiler/preprocessor/MacroExpander.cpp', 'src/compiler/preprocessor/Preprocessor.cpp', 'src/compiler/preprocessor/Token.cpp', 'src/compiler/preprocessor/Tokenizer.cpp', + 'src/compiler/translator/AddAndTrueToLoopCondition.cpp', 'src/compiler/translator/AddDefaultReturnStatements.cpp', 'src/compiler/translator/ArrayReturnValueToOutParameter.cpp', 'src/compiler/translator/ASTMetadataHLSL.cpp', 'src/compiler/translator/blocklayout.cpp', 'src/compiler/translator/blocklayoutHLSL.cpp', + 'src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp', 'src/compiler/translator/BuiltInFunctionEmulator.cpp', 'src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp', 'src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp', 'src/compiler/translator/Cache.cpp', 'src/compiler/translator/CallDAG.cpp', 'src/compiler/translator/CodeGen.cpp', 'src/compiler/translator/Compiler.cpp', + 'src/compiler/translator/ConstantUnion.cpp', 'src/compiler/translator/DeferGlobalInitializers.cpp', - 'src/compiler/translator/depgraph/DependencyGraph.cpp', - 'src/compiler/translator/depgraph/DependencyGraphBuilder.cpp', - 'src/compiler/translator/depgraph/DependencyGraphOutput.cpp', - 'src/compiler/translator/depgraph/DependencyGraphTraverse.cpp', 'src/compiler/translator/Diagnostics.cpp', 'src/compiler/translator/DirectiveHandler.cpp', 'src/compiler/translator/EmulatePrecision.cpp', 'src/compiler/translator/ExpandIntegerPowExpressions.cpp', 'src/compiler/translator/ExtensionGLSL.cpp', 'src/compiler/translator/FlagStd140Structs.cpp', 'src/compiler/translator/ForLoopUnroll.cpp', 'src/compiler/translator/InfoSink.cpp', @@ -66,16 +65,17 @@ UNIFIED_SOURCES += [ 'src/compiler/translator/Operator.cpp', 'src/compiler/translator/OutputESSL.cpp', 'src/compiler/translator/OutputGLSL.cpp', 'src/compiler/translator/OutputGLSLBase.cpp', 'src/compiler/translator/OutputHLSL.cpp', 'src/compiler/translator/ParseContext.cpp', 'src/compiler/translator/PoolAlloc.cpp', 'src/compiler/translator/PruneEmptyDeclarations.cpp', + 'src/compiler/translator/QualifierTypes.cpp', 'src/compiler/translator/RecordConstantPrecision.cpp', 'src/compiler/translator/RegenerateStructNames.cpp', 'src/compiler/translator/RemoveDynamicIndexing.cpp', 'src/compiler/translator/RemovePow.cpp', 'src/compiler/translator/RemoveSwitchFallThrough.cpp', 'src/compiler/translator/RewriteDoWhile.cpp', 'src/compiler/translator/RewriteElseBlocks.cpp', 'src/compiler/translator/RewriteTexelFetchOffset.cpp', @@ -86,18 +86,16 @@ UNIFIED_SOURCES += [ 'src/compiler/translator/SeparateExpressionsReturningArrays.cpp', 'src/compiler/translator/ShaderLang.cpp', 'src/compiler/translator/ShaderVars.cpp', 'src/compiler/translator/SimplifyLoopConditions.cpp', 'src/compiler/translator/SplitSequenceOperator.cpp', 'src/compiler/translator/StructureHLSL.cpp', 'src/compiler/translator/SymbolTable.cpp', 'src/compiler/translator/TextureFunctionHLSL.cpp', - 'src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp', - 'src/compiler/translator/timing/RestrictVertexShaderTiming.cpp', 'src/compiler/translator/TranslatorESSL.cpp', 'src/compiler/translator/TranslatorGLSL.cpp', 'src/compiler/translator/TranslatorHLSL.cpp', 'src/compiler/translator/Types.cpp', 'src/compiler/translator/UnfoldShortCircuitAST.cpp', 'src/compiler/translator/UnfoldShortCircuitToIf.cpp', 'src/compiler/translator/UniformHLSL.cpp', 'src/compiler/translator/util.cpp',
--- a/gfx/angle/src/commit.h +++ b/gfx/angle/src/commit.h @@ -1,3 +1,3 @@ -#define ANGLE_COMMIT_HASH "" +#define ANGLE_COMMIT_HASH "8b3e8b4d1b09" #define ANGLE_COMMIT_HASH_SIZE 12 -#define ANGLE_COMMIT_DATE "" +#define ANGLE_COMMIT_DATE "2016-10-24 15:38:47 +0800"
--- a/gfx/angle/src/compiler.gypi +++ b/gfx/angle/src/compiler.gypi @@ -17,27 +17,32 @@ '../include/GLES3/gl3.h', '../include/GLES3/gl3platform.h', '../include/GLES3/gl31.h', '../include/GLES3/gl32.h', '../include/GLSLANG/ShaderLang.h', '../include/GLSLANG/ShaderVars.h', '../include/KHR/khrplatform.h', '../include/angle_gl.h', + 'compiler/translator/AddAndTrueToLoopCondition.cpp', + 'compiler/translator/AddAndTrueToLoopCondition.h', 'compiler/translator/BaseTypes.h', 'compiler/translator/BuiltInFunctionEmulator.cpp', 'compiler/translator/BuiltInFunctionEmulator.h', + 'compiler/translator/BreakVariableAliasingInInnerLoops.cpp', + 'compiler/translator/BreakVariableAliasingInInnerLoops.h', 'compiler/translator/Cache.cpp', 'compiler/translator/Cache.h', 'compiler/translator/CallDAG.cpp', 'compiler/translator/CallDAG.h', 'compiler/translator/CodeGen.cpp', 'compiler/translator/Common.h', 'compiler/translator/Compiler.cpp', 'compiler/translator/Compiler.h', + 'compiler/translator/ConstantUnion.cpp', 'compiler/translator/ConstantUnion.h', 'compiler/translator/DeferGlobalInitializers.cpp', 'compiler/translator/DeferGlobalInitializers.h', 'compiler/translator/Diagnostics.cpp', 'compiler/translator/Diagnostics.h', 'compiler/translator/DirectiveHandler.cpp', 'compiler/translator/DirectiveHandler.h', 'compiler/translator/EmulateGLFragColorBroadcast.cpp', @@ -76,27 +81,28 @@ 'compiler/translator/Operator.h', 'compiler/translator/ParseContext.cpp', 'compiler/translator/ParseContext.h', 'compiler/translator/PoolAlloc.cpp', 'compiler/translator/PoolAlloc.h', 'compiler/translator/Pragma.h', 'compiler/translator/PruneEmptyDeclarations.cpp', 'compiler/translator/PruneEmptyDeclarations.h', + 'compiler/translator/QualifierTypes.h', + 'compiler/translator/QualifierTypes.cpp', 'compiler/translator/RecordConstantPrecision.cpp', 'compiler/translator/RecordConstantPrecision.h', 'compiler/translator/RegenerateStructNames.cpp', 'compiler/translator/RegenerateStructNames.h', 'compiler/translator/RemovePow.cpp', 'compiler/translator/RemovePow.h', 'compiler/translator/RewriteDoWhile.cpp', 'compiler/translator/RewriteDoWhile.h', 'compiler/translator/RewriteTexelFetchOffset.cpp', 'compiler/translator/RewriteTexelFetchOffset.h', - 'compiler/translator/RenameFunction.h', 'compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp', 'compiler/translator/ScalarizeVecAndMatConstructorArgs.h', 'compiler/translator/SearchSymbol.cpp', 'compiler/translator/SearchSymbol.h', 'compiler/translator/SymbolTable.cpp', 'compiler/translator/SymbolTable.h', 'compiler/translator/Types.cpp', 'compiler/translator/Types.h', @@ -113,35 +119,24 @@ 'compiler/translator/ValidateSwitch.cpp', 'compiler/translator/ValidateSwitch.h', 'compiler/translator/VariableInfo.cpp', 'compiler/translator/VariableInfo.h', 'compiler/translator/VariablePacker.cpp', 'compiler/translator/VariablePacker.h', 'compiler/translator/blocklayout.cpp', 'compiler/translator/blocklayout.h', - 'compiler/translator/depgraph/DependencyGraph.cpp', - 'compiler/translator/depgraph/DependencyGraph.h', - 'compiler/translator/depgraph/DependencyGraphBuilder.cpp', - 'compiler/translator/depgraph/DependencyGraphBuilder.h', - 'compiler/translator/depgraph/DependencyGraphOutput.cpp', - 'compiler/translator/depgraph/DependencyGraphOutput.h', - 'compiler/translator/depgraph/DependencyGraphTraverse.cpp', 'compiler/translator/glslang.h', 'compiler/translator/glslang.l', 'compiler/translator/glslang.y', 'compiler/translator/glslang_lex.cpp', 'compiler/translator/glslang_tab.cpp', 'compiler/translator/glslang_tab.h', 'compiler/translator/intermOut.cpp', 'compiler/translator/length_limits.h', - 'compiler/translator/timing/RestrictFragmentShaderTiming.cpp', - 'compiler/translator/timing/RestrictFragmentShaderTiming.h', - 'compiler/translator/timing/RestrictVertexShaderTiming.cpp', - 'compiler/translator/timing/RestrictVertexShaderTiming.h', 'compiler/translator/util.cpp', 'compiler/translator/util.h', 'third_party/compiler/ArrayBoundsClamper.cpp', 'third_party/compiler/ArrayBoundsClamper.h', ], 'angle_translator_lib_essl_sources': [ 'compiler/translator/OutputESSL.cpp',
new file mode 100644 --- /dev/null +++ b/gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp @@ -0,0 +1,59 @@ +// +// Copyright (c) 2016 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. +// + +#include "compiler/translator/AddAndTrueToLoopCondition.h" + +#include "compiler/translator/IntermNode.h" + +namespace sh +{ + +namespace +{ + +// An AST traverser that rewrites for and while loops by replacing "condition" with +// "condition && true" to work around condition bug on Intel Mac. +class AddAndTrueToLoopConditionTraverser : public TIntermTraverser +{ + public: + AddAndTrueToLoopConditionTraverser() : TIntermTraverser(true, false, false) {} + + bool visitLoop(Visit, TIntermLoop *loop) override + { + // do-while loop doesn't have this bug. + if (loop->getType() != ELoopFor && loop->getType() != ELoopWhile) + { + return true; + } + + // For loop may not have a condition. + if (loop->getCondition() == nullptr) + { + return true; + } + + // Constant true. + TConstantUnion *trueConstant = new TConstantUnion(); + trueConstant->setBConst(true); + TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, TType(EbtBool)); + + // CONDITION && true. + TIntermBinary *andOp = new TIntermBinary(EOpLogicalAnd, loop->getCondition(), trueValue); + loop->setCondition(andOp); + + return true; + } +}; + +} // anonymous namespace + +void AddAndTrueToLoopCondition(TIntermNode *root) +{ + AddAndTrueToLoopConditionTraverser traverser; + root->traverse(&traverser); +} + +} // namespace sh
new file mode 100644 --- /dev/null +++ b/gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.h @@ -0,0 +1,20 @@ +// +// Copyright (c) 2016 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. +// + +// Rewrite condition in for and while loops to work around driver bug on Intel Mac. + +#ifndef COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_ +#define COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_ + +class TIntermNode; +namespace sh +{ + +void AddAndTrueToLoopCondition(TIntermNode *root); + +} // namespace sh + +#endif // COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
--- a/gfx/angle/src/compiler/translator/AddDefaultReturnStatements.cpp +++ b/gfx/angle/src/compiler/translator/AddDefaultReturnStatements.cpp @@ -49,58 +49,23 @@ class AddDefaultReturnStatementsTraverse if (returnNode != nullptr && returnNode->getFlowOp() == EOpReturn) { return false; } return true; } - static TIntermTyped *GenerateTypeConstructor(const TType &returnType) - { - // Base case, constructing a single element - if (!returnType.isArray()) - { - size_t objectSize = returnType.getObjectSize(); - TConstantUnion *constantUnion = new TConstantUnion[objectSize]; - for (size_t constantIdx = 0; constantIdx < objectSize; constantIdx++) - { - constantUnion[constantIdx].setFConst(0.0f); - } - - TIntermConstantUnion *intermConstantUnion = - new TIntermConstantUnion(constantUnion, returnType); - return intermConstantUnion; - } - - // Recursive case, construct an array of single elements - TIntermAggregate *constructorAggrigate = - new TIntermAggregate(TypeToConstructorOperator(returnType)); - constructorAggrigate->setType(returnType); - - size_t arraySize = returnType.getArraySize(); - for (size_t arrayIdx = 0; arrayIdx < arraySize; arrayIdx++) - { - TType arrayElementType(returnType); - arrayElementType.clearArrayness(); - - constructorAggrigate->getSequence()->push_back( - GenerateTypeConstructor(arrayElementType)); - } - - return constructorAggrigate; - } - bool visitAggregate(Visit, TIntermAggregate *node) override { TType returnType; if (IsFunctionWithoutReturnStatement(node, &returnType)) { TIntermBranch *branch = - new TIntermBranch(EOpReturn, GenerateTypeConstructor(returnType)); + new TIntermBranch(EOpReturn, TIntermTyped::CreateZero(returnType)); TIntermAggregate *lastNode = node->getSequence()->back()->getAsAggregate(); lastNode->getSequence()->push_back(branch); return false; } return true;
--- a/gfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp +++ b/gfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp @@ -162,22 +162,21 @@ bool ArrayReturnValueToOutParameterTrave bool ArrayReturnValueToOutParameterTraverser::visitBranch(Visit visit, TIntermBranch *node) { if (mInFunctionWithArrayReturnValue && node->getFlowOp() == EOpReturn) { // Instead of returning a value, assign to the out parameter and then return. TIntermSequence replacements; - TIntermBinary *replacementAssignment = new TIntermBinary(EOpAssign); TIntermTyped *expression = node->getExpression(); ASSERT(expression != nullptr); - replacementAssignment->setLeft(CreateReturnValueSymbol(expression->getType())); - replacementAssignment->setRight(node->getExpression()); - replacementAssignment->setType(expression->getType()); + TIntermSymbol *returnValueSymbol = CreateReturnValueSymbol(expression->getType()); + TIntermBinary *replacementAssignment = + new TIntermBinary(EOpAssign, returnValueSymbol, expression); replacementAssignment->setLine(expression->getLine()); replacements.push_back(replacementAssignment); TIntermBranch *replacementBranch = new TIntermBranch(EOpReturn, nullptr); replacementBranch->setLine(node->getLine()); replacements.push_back(replacementBranch); mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(getParentNode()->getAsAggregate(), node, replacements));
--- a/gfx/angle/src/compiler/translator/BaseTypes.h +++ b/gfx/angle/src/compiler/translator/BaseTypes.h @@ -336,20 +336,21 @@ enum TQualifier EvqSecondaryFragColorEXT, // EXT_blend_func_extended EvqSecondaryFragDataEXT, // EXT_blend_func_extended // built-ins written by the shader_framebuffer_fetch extension(s) EvqLastFragColor, EvqLastFragData, // GLSL ES 3.0 vertex output and fragment input - EvqSmooth, // Incomplete qualifier, smooth is the default - EvqFlat, // Incomplete qualifier - EvqSmoothOut = EvqSmooth, - EvqFlatOut = EvqFlat, + EvqSmooth, // Incomplete qualifier, smooth is the default + EvqFlat, // Incomplete qualifier + EvqCentroid, // Incomplete qualifier + EvqSmoothOut, + EvqFlatOut, EvqCentroidOut, // Implies smooth EvqSmoothIn, EvqFlatIn, EvqCentroidIn, // Implies smooth // GLSL ES 3.1 compute shader special variables EvqComputeIn, EvqNumWorkGroups, @@ -358,16 +359,21 @@ enum TQualifier EvqLocalInvocationID, EvqGlobalInvocationID, EvqLocalInvocationIndex, // end of list EvqLast }; +inline bool IsQualifierUnspecified(TQualifier qualifier) +{ + return (qualifier == EvqTemporary || qualifier == EvqGlobal); +} + enum TLayoutMatrixPacking { EmpUnspecified, EmpRowMajor, EmpColumnMajor }; enum TLayoutBlockStorage @@ -376,27 +382,29 @@ enum TLayoutBlockStorage EbsShared, EbsPacked, EbsStd140 }; struct TLayoutQualifier { int location; + unsigned int locationsSpecified; TLayoutMatrixPacking matrixPacking; TLayoutBlockStorage blockStorage; // Compute shader layout qualifiers. sh::WorkGroupSize localSize; static TLayoutQualifier create() { TLayoutQualifier layoutQualifier; layoutQualifier.location = -1; + layoutQualifier.locationsSpecified = 0; layoutQualifier.matrixPacking = EmpUnspecified; layoutQualifier.blockStorage = EbsUnspecified; layoutQualifier.localSize.fill(-1); return layoutQualifier; } @@ -477,16 +485,19 @@ inline const char* getQualifierString(TQ case EvqLastFragColor: return "LastFragColor"; case EvqLastFragData: return "LastFragData"; case EvqSmoothOut: return "smooth out"; case EvqCentroidOut: return "smooth centroid out"; case EvqFlatOut: return "flat out"; case EvqSmoothIn: return "smooth in"; case EvqFlatIn: return "flat in"; case EvqCentroidIn: return "smooth centroid in"; + case EvqCentroid: return "centroid"; + case EvqFlat: return "flat"; + case EvqSmooth: return "smooth"; case EvqComputeIn: return "in"; case EvqNumWorkGroups: return "NumWorkGroups"; case EvqWorkGroupSize: return "WorkGroupSize"; case EvqWorkGroupID: return "WorkGroupID"; case EvqLocalInvocationID: return "LocalInvocationID"; case EvqGlobalInvocationID: return "GlobalInvocationID"; case EvqLocalInvocationIndex: return "LocalInvocationIndex"; default: UNREACHABLE(); return "unknown qualifier";
new file mode 100644 --- /dev/null +++ b/gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp @@ -0,0 +1,106 @@ +// +// Copyright (c) 2016 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. +// + +// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend +// may record a variable as aliasing another. Sometimes the alias information gets garbled +// so we work around this issue by breaking the aliasing chain in inner loops. + +#include "BreakVariableAliasingInInnerLoops.h" + +#include "compiler/translator/IntermNode.h" + +// A HLSL compiler developer gave us more details on the root cause and the workaround needed: +// The root problem is that if the HLSL compiler is applying aliasing information even on +// incomplete simulations (in this case, a single pass). The bug is triggered by an assignment +// that comes from a series of assignments, possibly with swizzled or ternary operators with +// known conditionals, where the source is before the loop. +// So, a workaround is to add a +0 term to variables the first time they are assigned to in +// an inner loop (if they are declared in an outside scope, otherwise there is no need). +// This will break the aliasing chain. + +// For simplicity here we add a +0 to any assignment that is in at least two nested loops. Because +// the bug only shows up with swizzles, and ternary assignment, whole array or whole structure +// assignment don't need a workaround. + +namespace +{ + +class AliasingBreaker : public TIntermTraverser +{ + public: + AliasingBreaker() : TIntermTraverser(true, false, true) {} + + protected: + bool visitBinary(Visit visit, TIntermBinary *binary) + { + if (visit != PreVisit) + { + return false; + } + + if (mLoopLevel < 2 || !binary->isAssignment()) + { + return true; + } + + TIntermTyped *B = binary->getRight(); + TType type = B->getType(); + + if (!type.isScalar() && !type.isVector() && !type.isMatrix()) + { + return true; + } + + if (type.isArray() || IsSampler(type.getBasicType())) + { + return true; + } + + // We have a scalar / vector / matrix assignment with loop depth 2. + // Transform it from + // A = B + // to + // A = (B + typeof<B>(0)); + + TIntermBinary *bPlusZero = new TIntermBinary(EOpAdd, B, TIntermTyped::CreateZero(type)); + bPlusZero->setLine(B->getLine()); + + binary->replaceChildNode(B, bPlusZero); + + return true; + } + + bool visitLoop(Visit visit, TIntermLoop *loop) + { + if (visit == PreVisit) + { + mLoopLevel++; + } + else + { + ASSERT(mLoopLevel > 0); + mLoopLevel--; + } + + return true; + } + + private: + int mLoopLevel = 0; +}; + +} // anonymous namespace + +namespace sh +{ + +void BreakVariableAliasingInInnerLoops(TIntermNode *root) +{ + AliasingBreaker breaker; + root->traverse(&breaker); +} + +} // namespace sh
new file mode 100644 --- /dev/null +++ b/gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h @@ -0,0 +1,23 @@ +// +// Copyright (c) 2016 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. +// + +// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend +// may record a variable as aliasing another. Sometimes the alias information gets garbled +// so we work around this issue by breaking the aliasing chain in inner loops. + +#ifndef COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_ +#define COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_ + +class TIntermNode; + +namespace sh +{ + +void BreakVariableAliasingInInnerLoops(TIntermNode *root); + +} // namespace sh + +#endif // COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
--- a/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp +++ b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp @@ -6,42 +6,24 @@ #include "angle_gl.h" #include "compiler/translator/BuiltInFunctionEmulator.h" #include "compiler/translator/BuiltInFunctionEmulatorGLSL.h" #include "compiler/translator/Cache.h" #include "compiler/translator/SymbolTable.h" #include "compiler/translator/VersionGLSL.h" -void InitBuiltInFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, sh::GLenum shaderType) +void InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, + sh::GLenum shaderType) { - // we use macros here instead of function definitions to work around more GLSL - // compiler bugs, in particular on NVIDIA hardware on Mac OSX. Macros are - // problematic because if the argument has side-effects they will be repeatedly - // evaluated. This is unlikely to show up in real shaders, but is something to - // consider. - - const TType *float1 = TCache::getType(EbtFloat); - const TType *float2 = TCache::getType(EbtFloat, 2); - const TType *float3 = TCache::getType(EbtFloat, 3); - const TType *float4 = TCache::getType(EbtFloat, 4); - - if (shaderType == GL_FRAGMENT_SHADER) + if (shaderType == GL_VERTEX_SHADER) { - emu->addEmulatedFunction(EOpCos, float1, "webgl_emu_precision float webgl_cos_emu(webgl_emu_precision float a) { return cos(a); }"); - emu->addEmulatedFunction(EOpCos, float2, "webgl_emu_precision vec2 webgl_cos_emu(webgl_emu_precision vec2 a) { return cos(a); }"); - emu->addEmulatedFunction(EOpCos, float3, "webgl_emu_precision vec3 webgl_cos_emu(webgl_emu_precision vec3 a) { return cos(a); }"); - emu->addEmulatedFunction(EOpCos, float4, "webgl_emu_precision vec4 webgl_cos_emu(webgl_emu_precision vec4 a) { return cos(a); }"); + const TType *int1 = TCache::getType(EbtInt); + emu->addEmulatedFunction(EOpAbs, int1, "int webgl_abs_emu(int x) { return x * sign(x); }"); } - emu->addEmulatedFunction(EOpDistance, float1, float1, "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))"); - emu->addEmulatedFunction(EOpDot, float1, float1, "#define webgl_dot_emu(x, y) ((x) * (y))"); - emu->addEmulatedFunction(EOpLength, float1, "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))"); - emu->addEmulatedFunction(EOpNormalize, float1, "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))"); - emu->addEmulatedFunction(EOpReflect, float1, float1, "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))"); - emu->addEmulatedFunction(EOpFaceForward, float1, float1, float1, "#define webgl_faceforward_emu(N, I, Nref) (((Nref) * (I) < 0.0) ? (N) : -(N))"); } // Emulate built-in functions missing from GLSL 1.30 and higher void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType, int targetGLSLVersion) { // Emulate packUnorm2x16 and unpackUnorm2x16 (GLSL 4.10) if (targetGLSLVersion < GLSL_VERSION_410) @@ -176,17 +158,19 @@ void InitBuiltInFunctionEmulatorForGLSLM " return uintBitsToFloat(sign | 0x7F800000u | mantissa);\n" " }\n" " else\n" " {\n" " exponent -= 15;\n" " float scale;\n" " if(exponent < 0)\n" " {\n" - " scale = 1.0 / (1 << -exponent);\n" + " // The negative unary operator is buggy on OSX.\n" + " // Work around this by using abs instead.\n" + " scale = 1.0 / (1 << abs(exponent));\n" " }\n" " else\n" " {\n" " scale = 1 << exponent;\n" " }\n" " float decimal = 1.0 + float(mantissa) / float(1 << 10);\n" " f32 = scale * decimal;\n" " }\n"
--- a/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h +++ b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h @@ -7,19 +7,20 @@ #ifndef COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_ #define COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_ #include "GLSLANG/ShaderLang.h" class BuiltInFunctionEmulator; // -// This is only a workaround for OpenGL driver bugs, and isn't needed in general. +// This works around bug in Intel Mac drivers. // -void InitBuiltInFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, sh::GLenum shaderType); +void InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, + sh::GLenum shaderType); // // This function is emulating built-in functions missing from GLSL 1.30 and higher. // void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType, int targetGLSLVersion); #endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
--- a/gfx/angle/src/compiler/translator/Compiler.cpp +++ b/gfx/angle/src/compiler/translator/Compiler.cpp @@ -1,47 +1,42 @@ // // Copyright (c) 2002-2014 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. // +#include "compiler/translator/AddAndTrueToLoopCondition.h" #include "compiler/translator/Cache.h" #include "compiler/translator/Compiler.h" #include "compiler/translator/CallDAG.h" #include "compiler/translator/DeferGlobalInitializers.h" #include "compiler/translator/EmulateGLFragColorBroadcast.h" #include "compiler/translator/ForLoopUnroll.h" #include "compiler/translator/Initialize.h" #include "compiler/translator/InitializeParseContext.h" #include "compiler/translator/InitializeVariables.h" #include "compiler/translator/ParseContext.h" #include "compiler/translator/PruneEmptyDeclarations.h" #include "compiler/translator/RegenerateStructNames.h" #include "compiler/translator/RemovePow.h" -#include "compiler/translator/RenameFunction.h" #include "compiler/translator/RewriteDoWhile.h" #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h" #include "compiler/translator/UnfoldShortCircuitAST.h" #include "compiler/translator/ValidateLimitations.h" #include "compiler/translator/ValidateMaxParameters.h" #include "compiler/translator/ValidateOutputs.h" #include "compiler/translator/VariablePacker.h" -#include "compiler/translator/depgraph/DependencyGraph.h" -#include "compiler/translator/depgraph/DependencyGraphOutput.h" -#include "compiler/translator/timing/RestrictFragmentShaderTiming.h" -#include "compiler/translator/timing/RestrictVertexShaderTiming.h" #include "third_party/compiler/ArrayBoundsClamper.h" #include "angle_gl.h" #include "common/utilities.h" bool IsWebGLBasedSpec(ShShaderSpec spec) { - return (spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_SPEC || spec == SH_WEBGL2_SPEC || - spec == SH_WEBGL3_SPEC); + return (spec == SH_WEBGL_SPEC || spec == SH_WEBGL2_SPEC || spec == SH_WEBGL3_SPEC); } bool IsGLSL130OrNewer(ShShaderOutput output) { return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT || output == SH_GLSL_150_CORE_OUTPUT || output == SH_GLSL_330_CORE_OUTPUT || @@ -55,17 +50,16 @@ bool IsGLSL130OrNewer(ShShaderOutput out size_t GetGlobalMaxTokenSize(ShShaderSpec spec) { // WebGL defines a max token legnth of 256, while ES2 leaves max token // size undefined. ES3 defines a max size of 1024 characters. switch (spec) { case SH_WEBGL_SPEC: - case SH_CSS_SHADERS_SPEC: return 256; default: return 1024; } } namespace { @@ -106,17 +100,16 @@ class TScopedSymbolTableLevel }; int MapSpecToShaderVersion(ShShaderSpec spec) { switch (spec) { case SH_GLES2_SPEC: case SH_WEBGL_SPEC: - case SH_CSS_SHADERS_SPEC: return 100; case SH_GLES3_SPEC: case SH_WEBGL2_SPEC: return 300; case SH_GLES3_1_SPEC: case SH_WEBGL3_SPEC: return 310; default: @@ -157,17 +150,17 @@ TCompiler::TCompiler(sh::GLenum type, Sh { mComputeShaderLocalSize.fill(1); } TCompiler::~TCompiler() { } -bool TCompiler::shouldRunLoopAndIndexingValidation(int compileOptions) const +bool TCompiler::shouldRunLoopAndIndexingValidation(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(shaderSpec) && shaderVersion == 100) || (compileOptions & SH_VALIDATE_LOOP_INDEXING); } @@ -192,25 +185,26 @@ bool TCompiler::Init(const ShBuiltInReso arrayBoundsClamper.SetClampingStrategy(resources.ArrayIndexClampingStrategy); clampingStrategy = resources.ArrayIndexClampingStrategy; hashFunction = resources.HashFunction; return true; } -TIntermNode *TCompiler::compileTreeForTesting(const char* const shaderStrings[], - size_t numStrings, int compileOptions) +TIntermNode *TCompiler::compileTreeForTesting(const char *const shaderStrings[], + size_t numStrings, + ShCompileOptions compileOptions) { return compileTreeImpl(shaderStrings, numStrings, compileOptions); } TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[], size_t numStrings, - const int compileOptions) + const ShCompileOptions compileOptions) { clearResults(); ASSERT(numStrings > 0); ASSERT(GetGlobalPoolAllocator()); // Reset the extension behavior for each compilation unit. ResetExtensionBehavior(extensionBehavior); @@ -218,18 +212,17 @@ TIntermNode *TCompiler::compileTreeImpl( // First string is path of source file if flag is set. The actual source follows. size_t firstSource = 0; if (compileOptions & SH_SOURCE_PATH) { mSourcePath = shaderStrings[0]; ++firstSource; } - TIntermediate intermediate(infoSink); - TParseContext parseContext(symbolTable, extensionBehavior, intermediate, shaderType, shaderSpec, + TParseContext parseContext(symbolTable, extensionBehavior, shaderType, shaderSpec, compileOptions, true, infoSink, getResources()); parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh); SetGlobalParseContext(&parseContext); // We preserve symbols at the built-in level from compile-to-compile. // Start pushing the user-defined symbols at global level. TScopedSymbolTableLevel scopedSymbolLevel(&symbolTable); @@ -253,17 +246,17 @@ TIntermNode *TCompiler::compileTreeImpl( { mPragma = parseContext.pragma(); symbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll); mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared(); mComputeShaderLocalSize = parseContext.getComputeShaderLocalSize(); root = parseContext.getTreeRoot(); - root = intermediate.postProcess(root); + root = TIntermediate::PostProcess(root); // Highp might have been auto-enabled based on shader version fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh(); // Disallow expressions deemed too complex. if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY)) success = limitExpressionComplexity(root); @@ -290,22 +283,16 @@ TIntermNode *TCompiler::compileTreeImpl( PruneEmptyDeclarations(root); if (success && shaderVersion == 300 && shaderType == GL_FRAGMENT_SHADER) success = validateOutputs(root); if (success && shouldRunLoopAndIndexingValidation(compileOptions)) success = validateLimitations(root); - if (success && (compileOptions & SH_TIMING_RESTRICTIONS)) - success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0); - - if (success && shaderSpec == SH_CSS_SHADERS_SPEC) - rewriteCSSShader(root); - // Unroll for-loop markup needs to happen after validateLimitations pass. if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX)) { ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex, shouldRunLoopAndIndexingValidation(compileOptions)); root->traverse(&marker); } if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX)) @@ -340,16 +327,19 @@ TIntermNode *TCompiler::compileTreeImpl( ((compileOptions & SH_INIT_GL_POSITION) || (outputType == SH_GLSL_COMPATIBILITY_OUTPUT))) initializeGLPosition(root); // This pass might emit short circuits so keep it before the short circuit unfolding if (success && (compileOptions & SH_REWRITE_DO_WHILE_LOOPS)) RewriteDoWhile(root, getTemporaryIndex()); + if (success && (compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION)) + sh::AddAndTrueToLoopCondition(root); + if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT)) { UnfoldShortCircuitAST unfoldShortCircuit; root->traverse(&unfoldShortCircuit); unfoldShortCircuit.updateTree(); } if (success && (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT)) @@ -403,22 +393,24 @@ TIntermNode *TCompiler::compileTreeImpl( SetGlobalParseContext(NULL); if (success) return root; return NULL; } -bool TCompiler::compile(const char *const shaderStrings[], size_t numStrings, int compileOptionsIn) +bool TCompiler::compile(const char *const shaderStrings[], + size_t numStrings, + ShCompileOptions compileOptionsIn) { if (numStrings == 0) return true; - int compileOptions = compileOptionsIn; + ShCompileOptions compileOptions = compileOptionsIn; // Apply key workarounds. if (shouldFlattenPragmaStdglInvariantAll()) { // This should be harmless to do in all cases, but for the moment, do it only conditionally. compileOptions |= SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL; } @@ -447,25 +439,23 @@ bool TCompiler::InitBuiltInSymbolTable(c assert(symbolTable.isEmpty()); symbolTable.push(); // COMMON_BUILTINS symbolTable.push(); // ESSL1_BUILTINS symbolTable.push(); // ESSL3_BUILTINS symbolTable.push(); // ESSL3_1_BUILTINS TPublicType integer; - integer.type = EbtInt; - integer.primarySize = 1; - integer.secondarySize = 1; + integer.setBasicType(EbtInt); + integer.initializeSizeForScalarTypes(); integer.array = false; TPublicType floatingPoint; - floatingPoint.type = EbtFloat; - floatingPoint.primarySize = 1; - floatingPoint.secondarySize = 1; + floatingPoint.setBasicType(EbtFloat); + floatingPoint.initializeSizeForScalarTypes(); floatingPoint.array = false; switch(shaderType) { case GL_FRAGMENT_SHADER: symbolTable.setDefaultPrecision(integer, EbpMedium); break; case GL_VERTEX_SHADER: @@ -495,20 +485,19 @@ bool TCompiler::InitBuiltInSymbolTable(c return true; } void TCompiler::initSamplerDefaultPrecision(TBasicType samplerType) { ASSERT(samplerType > EbtGuardSamplerBegin && samplerType < EbtGuardSamplerEnd); TPublicType sampler; - sampler.primarySize = 1; - sampler.secondarySize = 1; + sampler.initializeSizeForScalarTypes(); + sampler.setBasicType(samplerType); sampler.array = false; - sampler.type = samplerType; symbolTable.setDefaultPrecision(sampler, EbpLow); } void TCompiler::setResourceString() { std::ostringstream strstream; // clang-format off @@ -758,59 +747,23 @@ bool TCompiler::pruneUnusedFunctions(TIn bool TCompiler::validateOutputs(TIntermNode* root) { ValidateOutputs validateOutputs(getExtensionBehavior(), compileResources.MaxDrawBuffers); root->traverse(&validateOutputs); return (validateOutputs.validateAndCountErrors(infoSink.info) == 0); } -void TCompiler::rewriteCSSShader(TIntermNode* root) -{ - RenameFunction renamer("main(", "css_main("); - root->traverse(&renamer); -} - bool TCompiler::validateLimitations(TIntermNode* root) { ValidateLimitations validate(shaderType, &infoSink.info); root->traverse(&validate); return validate.numErrors() == 0; } -bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph) -{ - if (shaderSpec != SH_WEBGL_SPEC) - { - infoSink.info << "Timing restrictions must be enforced under the WebGL spec."; - return false; - } - - if (shaderType == GL_FRAGMENT_SHADER) - { - TDependencyGraph graph(root); - - // Output any errors first. - bool success = enforceFragmentShaderTimingRestrictions(graph); - - // Then, output the dependency graph. - if (outputGraph) - { - TDependencyGraphOutput output(infoSink.info); - output.outputAllSpanningTrees(graph); - } - - return success; - } - else - { - return enforceVertexShaderTimingRestrictions(root); - } -} - bool TCompiler::limitExpressionComplexity(TIntermNode* root) { TMaxDepthTraverser traverser(maxExpressionComplexity+1); root->traverse(&traverser); if (traverser.getMaxDepth() > maxExpressionComplexity) { infoSink.info << "Expression too complex."; @@ -821,30 +774,16 @@ bool TCompiler::limitExpressionComplexit { infoSink.info << "Function has too many parameters."; return false; } return true; } -bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph) -{ - RestrictFragmentShaderTiming restrictor(infoSink.info); - restrictor.enforceRestrictions(graph); - return restrictor.numErrors() == 0; -} - -bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root) -{ - RestrictVertexShaderTiming restrictor(infoSink.info); - restrictor.enforceRestrictions(root); - return restrictor.numErrors() == 0; -} - void TCompiler::collectVariables(TIntermNode* root) { if (!variablesCollected) { sh::CollectVariables collect(&attributes, &outputVariables, &uniforms, &varyings, &interfaceBlocks, hashFunction, symbolTable, extensionBehavior); root->traverse(&collect); @@ -915,17 +854,17 @@ ShArrayIndexClampingStrategy TCompiler:: return clampingStrategy; } const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const { return builtInFunctionEmulator; } -void TCompiler::writePragma(int compileOptions) +void TCompiler::writePragma(ShCompileOptions compileOptions) { if (!(compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL)) { TInfoSinkBase &sink = infoSink.obj; if (mPragma.stdgl.invariantAll) sink << "#pragma STDGL invariant(all)\n"; } }
--- a/gfx/angle/src/compiler/translator/Compiler.h +++ b/gfx/angle/src/compiler/translator/Compiler.h @@ -20,24 +20,22 @@ #include "compiler/translator/HashNames.h" #include "compiler/translator/InfoSink.h" #include "compiler/translator/Pragma.h" #include "compiler/translator/SymbolTable.h" #include "compiler/translator/VariableInfo.h" #include "third_party/compiler/ArrayBoundsClamper.h" class TCompiler; -class TDependencyGraph; #ifdef ANGLE_ENABLE_HLSL class TranslatorHLSL; #endif // ANGLE_ENABLE_HLSL // -// Helper function to identify specs that are based on the WebGL spec, -// like the CSS Shaders spec. +// Helper function to identify specs that are based on the WebGL spec. // bool IsWebGLBasedSpec(ShShaderSpec spec); // // Helper function to check if the shader type is GLSL. // bool IsGLSL130OrNewer(ShShaderOutput output); @@ -70,21 +68,23 @@ class TCompiler : public TShHandleBase ~TCompiler() override; TCompiler *getAsCompiler() override { return this; } bool Init(const ShBuiltInResources& resources); // compileTreeForTesting should be used only when tests require access to // the AST. Users of this function need to manually manage the global pool // allocator. Returns NULL whenever there are compilation errors. - TIntermNode *compileTreeForTesting(const char* const shaderStrings[], - size_t numStrings, int compileOptions); + TIntermNode *compileTreeForTesting(const char *const shaderStrings[], + size_t numStrings, + ShCompileOptions compileOptions); - bool compile(const char* const shaderStrings[], - size_t numStrings, int compileOptions); + bool compile(const char *const shaderStrings[], + size_t numStrings, + ShCompileOptions compileOptions); // Get results of the last compilation. int getShaderVersion() const { return shaderVersion; } TInfoSink& getInfoSink() { return infoSink; } bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; } const sh::WorkGroupSize &getComputeShaderLocalSize() { return mComputeShaderLocalSize; } @@ -99,76 +99,68 @@ class TCompiler : public TShHandleBase ShHashFunction64 getHashFunction() const { return hashFunction; } NameMap& getNameMap() { return nameMap; } TSymbolTable& getSymbolTable() { return symbolTable; } ShShaderSpec getShaderSpec() const { return shaderSpec; } ShShaderOutput getOutputType() const { return outputType; } const std::string &getBuiltInResourcesString() const { return builtInResourcesString; } - bool shouldRunLoopAndIndexingValidation(int compileOptions) const; + bool shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const; // Get the resources set by InitBuiltInSymbolTable const ShBuiltInResources& getResources() const; protected: sh::GLenum getShaderType() const { return shaderType; } // Initialize symbol-table with built-in symbols. bool InitBuiltInSymbolTable(const ShBuiltInResources& resources); // Compute the string representation of the built-in resources void setResourceString(); // Return false if the call depth is exceeded. bool checkCallDepth(); // Returns true if a program has no conflicting or missing fragment outputs bool validateOutputs(TIntermNode* root); - // Rewrites a shader's intermediate tree according to the CSS Shaders spec. - void rewriteCSSShader(TIntermNode* root); // Returns true if the given shader does not exceed the minimum // functionality mandated in GLSL 1.0 spec Appendix A. bool validateLimitations(TIntermNode* root); // Collect info for all attribs, uniforms, varyings. void collectVariables(TIntermNode* root); // Add emulated functions to the built-in function emulator. - virtual void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions) {}; + virtual void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, + ShCompileOptions compileOptions){}; // Translate to object code. - virtual void translate(TIntermNode *root, int compileOptions) = 0; + virtual void translate(TIntermNode *root, ShCompileOptions compileOptions) = 0; // Returns true if, after applying the packing rules in the GLSL 1.017 spec // Appendix A, section 7, the shader does not use too many uniforms. bool enforcePackingRestrictions(); // Insert statements to initialize output variables in the beginning of main(). // This is to avoid undefined behaviors. void initializeOutputVariables(TIntermNode *root); // Insert gl_Position = vec4(0,0,0,0) to the beginning of main(). // It is to work around a Linux driver bug where missing this causes compile failure // while spec says it is allowed. // This function should only be applied to vertex shaders. void initializeGLPosition(TIntermNode* root); - // Returns true if the shader passes the restrictions that aim to prevent timing attacks. - bool enforceTimingRestrictions(TIntermNode* root, bool outputGraph); - // Returns true if the shader does not use samplers. - bool enforceVertexShaderTimingRestrictions(TIntermNode* root); - // Returns true if the shader does not use sampler dependent values to affect control - // flow or in operations whose time can depend on the input values. - bool enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph); // Return true if the maximum expression complexity is below the limit. bool limitExpressionComplexity(TIntermNode* root); // Get built-in extensions with default behavior. const TExtensionBehavior& getExtensionBehavior() const; const char *getSourcePath() const; const TPragma& getPragma() const { return mPragma; } - void writePragma(int compileOptions); + void writePragma(ShCompileOptions compileOptions); unsigned int *getTemporaryIndex() { return &mTemporaryIndex; } // Relies on collectVariables having been called. bool isVaryingDefined(const char *varyingName); const ArrayBoundsClamper& getArrayBoundsClamper() const; ShArrayIndexClampingStrategy getArrayIndexClampingStrategy() const; const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const; - virtual bool shouldCollectVariables(int compileOptions) + virtual bool shouldCollectVariables(ShCompileOptions compileOptions) { return (compileOptions & SH_VARIABLES) != 0; } virtual bool shouldFlattenPragmaStdglInvariantAll() = 0; std::vector<sh::Attribute> attributes; std::vector<sh::OutputVariable> outputVariables; @@ -188,17 +180,17 @@ class TCompiler : public TShHandleBase void initSamplerDefaultPrecision(TBasicType samplerType); // Removes unused function declarations and prototypes from the AST class UnusedPredicate; bool pruneUnusedFunctions(TIntermNode *root); TIntermNode *compileTreeImpl(const char *const shaderStrings[], size_t numStrings, - const int compileOptions); + const ShCompileOptions compileOptions); sh::GLenum shaderType; ShShaderSpec shaderSpec; ShShaderOutput outputType; struct FunctionMetadata { FunctionMetadata()
new file mode 100644 --- /dev/null +++ b/gfx/angle/src/compiler/translator/ConstantUnion.cpp @@ -0,0 +1,443 @@ +// +// Copyright 2016 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. +// +// ConstantUnion: Constant folding helper class. + +#include "compiler/translator/ConstantUnion.h" + +#include "compiler/translator/Diagnostics.h" + +TConstantUnion::TConstantUnion() +{ + iConst = 0; + type = EbtVoid; +} + +bool TConstantUnion::cast(TBasicType newType, const TConstantUnion &constant) +{ + switch (newType) + { + case EbtFloat: + switch (constant.type) + { + case EbtInt: + setFConst(static_cast<float>(constant.getIConst())); + break; + case EbtUInt: + setFConst(static_cast<float>(constant.getUConst())); + break; + case EbtBool: + setFConst(static_cast<float>(constant.getBConst())); + break; + case EbtFloat: + setFConst(static_cast<float>(constant.getFConst())); + break; + default: + return false; + } + break; + case EbtInt: + switch (constant.type) + { + case EbtInt: + setIConst(static_cast<int>(constant.getIConst())); + break; + case EbtUInt: + setIConst(static_cast<int>(constant.getUConst())); + break; + case EbtBool: + setIConst(static_cast<int>(constant.getBConst())); + break; + case EbtFloat: + setIConst(static_cast<int>(constant.getFConst())); + break; + default: + return false; + } + break; + case EbtUInt: + switch (constant.type) + { + case EbtInt: + setUConst(static_cast<unsigned int>(constant.getIConst())); + break; + case EbtUInt: + setUConst(static_cast<unsigned int>(constant.getUConst())); + break; + case EbtBool: + setUConst(static_cast<unsigned int>(constant.getBConst())); + break; + case EbtFloat: + setUConst(static_cast<unsigned int>(constant.getFConst())); + break; + default: + return false; + } + break; + case EbtBool: + switch (constant.type) + { + case EbtInt: + setBConst(constant.getIConst() != 0); + break; + case EbtUInt: + setBConst(constant.getUConst() != 0); + break; + case EbtBool: + setBConst(constant.getBConst()); + break; + case EbtFloat: + setBConst(constant.getFConst() != 0.0f); + break; + default: + return false; + } + break; + case EbtStruct: // Struct fields don't get cast + switch (constant.type) + { + case EbtInt: + setIConst(constant.getIConst()); + break; + case EbtUInt: + setUConst(constant.getUConst()); + break; + case EbtBool: + setBConst(constant.getBConst()); + break; + case EbtFloat: + setFConst(constant.getFConst()); + break; + default: + return false; + } + break; + default: + return false; + } + + return true; +} + +bool TConstantUnion::operator==(const int i) const +{ + return i == iConst; +} + +bool TConstantUnion::operator==(const unsigned int u) const +{ + return u == uConst; +} + +bool TConstantUnion::operator==(const float f) const +{ + return f == fConst; +} + +bool TConstantUnion::operator==(const bool b) const +{ + return b == bConst; +} + +bool TConstantUnion::operator==(const TConstantUnion &constant) const +{ + if (constant.type != type) + return false; + + switch (type) + { + case EbtInt: + return constant.iConst == iConst; + case EbtUInt: + return constant.uConst == uConst; + case EbtFloat: + return constant.fConst == fConst; + case EbtBool: + return constant.bConst == bConst; + default: + return false; + } +} + +bool TConstantUnion::operator!=(const int i) const +{ + return !operator==(i); +} + +bool TConstantUnion::operator!=(const unsigned int u) const +{ + return !operator==(u); +} + +bool TConstantUnion::operator!=(const float f) const +{ + return !operator==(f); +} + +bool TConstantUnion::operator!=(const bool b) const +{ + return !operator==(b); +} + +bool TConstantUnion::operator!=(const TConstantUnion &constant) const +{ + return !operator==(constant); +} + +bool TConstantUnion::operator>(const TConstantUnion &constant) const +{ + ASSERT(type == constant.type); + switch (type) + { + case EbtInt: + return iConst > constant.iConst; + case EbtUInt: + return uConst > constant.uConst; + case EbtFloat: + return fConst > constant.fConst; + default: + return false; // Invalid operation, handled at semantic analysis + } +} + +bool TConstantUnion::operator<(const TConstantUnion &constant) const +{ + ASSERT(type == constant.type); + switch (type) + { + case EbtInt: + return iConst < constant.iConst; + case EbtUInt: + return uConst < constant.uConst; + case EbtFloat: + return fConst < constant.fConst; + default: + return false; // Invalid operation, handled at semantic analysis + } +} + +// static +TConstantUnion TConstantUnion::add(const TConstantUnion &lhs, + const TConstantUnion &rhs, + TDiagnostics *diag) +{ + TConstantUnion returnValue; + ASSERT(lhs.type == rhs.type); + switch (lhs.type) + { + case EbtInt: + returnValue.setIConst(lhs.iConst + rhs.iConst); + break; + case EbtUInt: + returnValue.setUConst(lhs.uConst + rhs.uConst); + break; + case EbtFloat: + returnValue.setFConst(lhs.fConst + rhs.fConst); + break; + default: + UNREACHABLE(); + } + + return returnValue; +} + +// static +TConstantUnion TConstantUnion::sub(const TConstantUnion &lhs, + const TConstantUnion &rhs, + TDiagnostics *diag) +{ + TConstantUnion returnValue; + ASSERT(lhs.type == rhs.type); + switch (lhs.type) + { + case EbtInt: + returnValue.setIConst(lhs.iConst - rhs.iConst); + break; + case EbtUInt: + returnValue.setUConst(lhs.uConst - rhs.uConst); + break; + case EbtFloat: + returnValue.setFConst(lhs.fConst - rhs.fConst); + break; + default: + UNREACHABLE(); + } + + return returnValue; +} + +// static +TConstantUnion TConstantUnion::mul(const TConstantUnion &lhs, + const TConstantUnion &rhs, + TDiagnostics *diag) +{ + TConstantUnion returnValue; + ASSERT(lhs.type == rhs.type); + switch (lhs.type) + { + case EbtInt: + returnValue.setIConst(lhs.iConst * rhs.iConst); + break; + case EbtUInt: + returnValue.setUConst(lhs.uConst * rhs.uConst); + break; + case EbtFloat: + returnValue.setFConst(lhs.fConst * rhs.fConst); + break; + default: + UNREACHABLE(); + } + + return returnValue; +} + +TConstantUnion TConstantUnion::operator%(const TConstantUnion &constant) const +{ + TConstantUnion returnValue; + ASSERT(type == constant.type); + switch (type) + { + case EbtInt: + returnValue.setIConst(iConst % constant.iConst); + break; + case EbtUInt: + returnValue.setUConst(uConst % constant.uConst); + break; + default: + UNREACHABLE(); + } + + return returnValue; +} + +TConstantUnion TConstantUnion::operator>>(const TConstantUnion &constant) const +{ + TConstantUnion returnValue; + ASSERT(type == constant.type); + switch (type) + { + case EbtInt: + returnValue.setIConst(iConst >> constant.iConst); + break; + case EbtUInt: + returnValue.setUConst(uConst >> constant.uConst); + break; + default: + UNREACHABLE(); + } + + return returnValue; +} + +TConstantUnion TConstantUnion::operator<<(const TConstantUnion &constant) const +{ + TConstantUnion returnValue; + // The signedness of the second parameter might be different, but we + // don't care, since the result is undefined if the second parameter is + // negative, and aliasing should not be a problem with unions. + ASSERT(constant.type == EbtInt || constant.type == EbtUInt); + switch (type) + { + case EbtInt: + returnValue.setIConst(iConst << constant.iConst); + break; + case EbtUInt: + returnValue.setUConst(uConst << constant.uConst); + break; + default: + UNREACHABLE(); + } + + return returnValue; +} + +TConstantUnion TConstantUnion::operator&(const TConstantUnion &constant) const +{ + TConstantUnion returnValue; + ASSERT(constant.type == EbtInt || constant.type == EbtUInt); + switch (type) + { + case EbtInt: + returnValue.setIConst(iConst & constant.iConst); + break; + case EbtUInt: + returnValue.setUConst(uConst & constant.uConst); + break; + default: + UNREACHABLE(); + } + + return returnValue; +} + +TConstantUnion TConstantUnion::operator|(const TConstantUnion &constant) const +{ + TConstantUnion returnValue; + ASSERT(type == constant.type); + switch (type) + { + case EbtInt: + returnValue.setIConst(iConst | constant.iConst); + break; + case EbtUInt: + returnValue.setUConst(uConst | constant.uConst); + break; + default: + UNREACHABLE(); + } + + return returnValue; +} + +TConstantUnion TConstantUnion::operator^(const TConstantUnion &constant) const +{ + TConstantUnion returnValue; + ASSERT(type == constant.type); + switch (type) + { + case EbtInt: + returnValue.setIConst(iConst ^ constant.iConst); + break; + case EbtUInt: + returnValue.setUConst(uConst ^ constant.uConst); + break; + default: + UNREACHABLE(); + } + + return returnValue; +} + +TConstantUnion TConstantUnion::operator&&(const TConstantUnion &constant) const +{ + TConstantUnion returnValue; + ASSERT(type == constant.type); + switch (type) + { + case EbtBool: + returnValue.setBConst(bConst && constant.bConst); + break; + default: + UNREACHABLE(); + } + + return returnValue; +} + +TConstantUnion TConstantUnion::operator||(const TConstantUnion &constant) const +{ + TConstantUnion returnValue; + ASSERT(type == constant.type); + switch (type) + { + case EbtBool: + returnValue.setBConst(bConst || constant.bConst); + break; + default: + UNREACHABLE(); + } + + return returnValue; +}
--- a/gfx/angle/src/compiler/translator/ConstantUnion.h +++ b/gfx/angle/src/compiler/translator/ConstantUnion.h @@ -4,345 +4,74 @@ // found in the LICENSE file. // #ifndef COMPILER_TRANSLATOR_CONSTANTUNION_H_ #define COMPILER_TRANSLATOR_CONSTANTUNION_H_ #include <assert.h> +#include "compiler/translator/Common.h" #include "compiler/translator/BaseTypes.h" -class TConstantUnion { -public: - POOL_ALLOCATOR_NEW_DELETE(); - TConstantUnion() - { - iConst = 0; - type = EbtVoid; - } +class TDiagnostics; - bool cast(TBasicType newType, const TConstantUnion &constant) - { - switch (newType) - { - case EbtFloat: - switch (constant.type) - { - case EbtInt: setFConst(static_cast<float>(constant.getIConst())); break; - case EbtUInt: setFConst(static_cast<float>(constant.getUConst())); break; - case EbtBool: setFConst(static_cast<float>(constant.getBConst())); break; - case EbtFloat: setFConst(static_cast<float>(constant.getFConst())); break; - default: return false; - } - break; - case EbtInt: - switch (constant.type) - { - case EbtInt: setIConst(static_cast<int>(constant.getIConst())); break; - case EbtUInt: setIConst(static_cast<int>(constant.getUConst())); break; - case EbtBool: setIConst(static_cast<int>(constant.getBConst())); break; - case EbtFloat: setIConst(static_cast<int>(constant.getFConst())); break; - default: return false; - } - break; - case EbtUInt: - switch (constant.type) - { - case EbtInt: setUConst(static_cast<unsigned int>(constant.getIConst())); break; - case EbtUInt: setUConst(static_cast<unsigned int>(constant.getUConst())); break; - case EbtBool: setUConst(static_cast<unsigned int>(constant.getBConst())); break; - case EbtFloat: setUConst(static_cast<unsigned int>(constant.getFConst())); break; - default: return false; - } - break; - case EbtBool: - switch (constant.type) - { - case EbtInt: setBConst(constant.getIConst() != 0); break; - case EbtUInt: setBConst(constant.getUConst() != 0); break; - case EbtBool: setBConst(constant.getBConst()); break; - case EbtFloat: setBConst(constant.getFConst() != 0.0f); break; - default: return false; - } - break; - case EbtStruct: // Struct fields don't get cast - switch (constant.type) - { - case EbtInt: setIConst(constant.getIConst()); break; - case EbtUInt: setUConst(constant.getUConst()); break; - case EbtBool: setBConst(constant.getBConst()); break; - case EbtFloat: setFConst(constant.getFConst()); break; - default: return false; - } - break; - default: - return false; - } +class TConstantUnion +{ + public: + POOL_ALLOCATOR_NEW_DELETE(); + TConstantUnion(); - return true; - } + bool cast(TBasicType newType, const TConstantUnion &constant); void setIConst(int i) {iConst = i; type = EbtInt; } void setUConst(unsigned int u) { uConst = u; type = EbtUInt; } void setFConst(float f) {fConst = f; type = EbtFloat; } void setBConst(bool b) {bConst = b; type = EbtBool; } int getIConst() const { return iConst; } unsigned int getUConst() const { return uConst; } float getFConst() const { return fConst; } bool getBConst() const { return bConst; } - bool operator==(const int i) const - { - return i == iConst; - } - - bool operator==(const unsigned int u) const - { - return u == uConst; - } - - bool operator==(const float f) const - { - return f == fConst; - } - - bool operator==(const bool b) const - { - return b == bConst; - } - - bool operator==(const TConstantUnion& constant) const - { - if (constant.type != type) - return false; - - switch (type) { - case EbtInt: - return constant.iConst == iConst; - case EbtUInt: - return constant.uConst == uConst; - case EbtFloat: - return constant.fConst == fConst; - case EbtBool: - return constant.bConst == bConst; - default: - return false; - } - } - - bool operator!=(const int i) const - { - return !operator==(i); - } - - bool operator!=(const unsigned int u) const - { - return !operator==(u); - } - - bool operator!=(const float f) const - { - return !operator==(f); - } - - bool operator!=(const bool b) const - { - return !operator==(b); - } - - bool operator!=(const TConstantUnion& constant) const - { - return !operator==(constant); - } - - bool operator>(const TConstantUnion& constant) const - { - assert(type == constant.type); - switch (type) { - case EbtInt: - return iConst > constant.iConst; - case EbtUInt: - return uConst > constant.uConst; - case EbtFloat: - return fConst > constant.fConst; - default: - return false; // Invalid operation, handled at semantic analysis - } - } - - bool operator<(const TConstantUnion& constant) const - { - assert(type == constant.type); - switch (type) { - case EbtInt: - return iConst < constant.iConst; - case EbtUInt: - return uConst < constant.uConst; - case EbtFloat: - return fConst < constant.fConst; - default: - return false; // Invalid operation, handled at semantic analysis - } - } - - TConstantUnion operator+(const TConstantUnion& constant) const - { - TConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst + constant.iConst); break; - case EbtUInt: returnValue.setUConst(uConst + constant.uConst); break; - case EbtFloat: returnValue.setFConst(fConst + constant.fConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - TConstantUnion operator-(const TConstantUnion& constant) const - { - TConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst - constant.iConst); break; - case EbtUInt: returnValue.setUConst(uConst - constant.uConst); break; - case EbtFloat: returnValue.setFConst(fConst - constant.fConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - TConstantUnion operator*(const TConstantUnion& constant) const - { - TConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst * constant.iConst); break; - case EbtUInt: returnValue.setUConst(uConst * constant.uConst); break; - case EbtFloat: returnValue.setFConst(fConst * constant.fConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - TConstantUnion operator%(const TConstantUnion& constant) const - { - TConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst % constant.iConst); break; - case EbtUInt: returnValue.setUConst(uConst % constant.uConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - TConstantUnion operator>>(const TConstantUnion& constant) const - { - TConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst >> constant.iConst); break; - case EbtUInt: returnValue.setUConst(uConst >> constant.uConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - TConstantUnion operator<<(const TConstantUnion& constant) const - { - TConstantUnion returnValue; - // The signedness of the second parameter might be different, but we - // don't care, since the result is undefined if the second parameter is - // negative, and aliasing should not be a problem with unions. - assert(constant.type == EbtInt || constant.type == EbtUInt); - switch (type) { - case EbtInt: returnValue.setIConst(iConst << constant.iConst); break; - case EbtUInt: returnValue.setUConst(uConst << constant.uConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - TConstantUnion operator&(const TConstantUnion& constant) const - { - TConstantUnion returnValue; - assert(constant.type == EbtInt || constant.type == EbtUInt); - switch (type) { - case EbtInt: returnValue.setIConst(iConst & constant.iConst); break; - case EbtUInt: returnValue.setUConst(uConst & constant.uConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - TConstantUnion operator|(const TConstantUnion& constant) const - { - TConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst | constant.iConst); break; - case EbtUInt: returnValue.setUConst(uConst | constant.uConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - TConstantUnion operator^(const TConstantUnion& constant) const - { - TConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst ^ constant.iConst); break; - case EbtUInt: returnValue.setUConst(uConst ^ constant.uConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - TConstantUnion operator&&(const TConstantUnion& constant) const - { - TConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtBool: returnValue.setBConst(bConst && constant.bConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - TConstantUnion operator||(const TConstantUnion& constant) const - { - TConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtBool: returnValue.setBConst(bConst || constant.bConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } + bool operator==(const int i) const; + bool operator==(const unsigned int u) const; + bool operator==(const float f) const; + bool operator==(const bool b) const; + bool operator==(const TConstantUnion &constant) const; + bool operator!=(const int i) const; + bool operator!=(const unsigned int u) const; + bool operator!=(const float f) const; + bool operator!=(const bool b) const; + bool operator!=(const TConstantUnion &constant) const; + bool operator>(const TConstantUnion &constant) const; + bool operator<(const TConstantUnion &constant) const; + static TConstantUnion add(const TConstantUnion &lhs, + const TConstantUnion &rhs, + TDiagnostics *diag); + static TConstantUnion sub(const TConstantUnion &lhs, + const TConstantUnion &rhs, + TDiagnostics *diag); + static TConstantUnion mul(const TConstantUnion &lhs, + const TConstantUnion &rhs, + TDiagnostics *diag); + TConstantUnion operator%(const TConstantUnion &constant) const; + TConstantUnion operator>>(const TConstantUnion &constant) const; + TConstantUnion operator<<(const TConstantUnion &constant) const; + TConstantUnion operator&(const TConstantUnion &constant) const; + TConstantUnion operator|(const TConstantUnion &constant) const; + TConstantUnion operator^(const TConstantUnion &constant) const; + TConstantUnion operator&&(const TConstantUnion &constant) const; + TConstantUnion operator||(const TConstantUnion &constant) const; TBasicType getType() const { return type; } -private: - - union { - int iConst; // used for ivec, scalar ints - unsigned int uConst; // used for uvec, scalar uints - bool bConst; // used for bvec, scalar bools - float fConst; // used for vec, mat, scalar floats - } ; + private: + union { + int iConst; // used for ivec, scalar ints + unsigned int uConst; // used for uvec, scalar uints + bool bConst; // used for bvec, scalar bools + float fConst; // used for vec, mat, scalar floats + }; TBasicType type; }; #endif // COMPILER_TRANSLATOR_CONSTANTUNION_H_
--- a/gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp +++ b/gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp @@ -96,20 +96,18 @@ bool DeferGlobalInitializersTraverser::v (expression->getAsConstantUnion() == nullptr && !expression->isConstructorWithOnlyConstantUnionParameters()))) { // For variables which are not constant, defer their real initialization until // after we initialize uniforms. // Deferral is done also in any cases where the variable has not been constant folded, // since otherwise there's a chance that HLSL output will generate extra statements // from the initializer expression. - TIntermBinary *deferredInit = new TIntermBinary(EOpAssign); - deferredInit->setLeft(symbolNode->deepCopy()); - deferredInit->setRight(node->getRight()); - deferredInit->setType(node->getType()); + TIntermBinary *deferredInit = + new TIntermBinary(EOpAssign, symbolNode->deepCopy(), node->getRight()); mDeferredInitializers.push_back(deferredInit); // Change const global to a regular global if its initialization is deferred. // This can happen if ANGLE has not been able to fold the constant expression used // as an initializer. ASSERT(symbolNode->getQualifier() == EvqConst || symbolNode->getQualifier() == EvqGlobal); if (symbolNode->getQualifier() == EvqConst)
--- a/gfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp +++ b/gfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp @@ -12,66 +12,64 @@ // #include "compiler/translator/EmulateGLFragColorBroadcast.h" #include "compiler/translator/IntermNode.h" namespace { -TIntermConstantUnion *constructIndexNode(int index) -{ - TConstantUnion *u = new TConstantUnion[1]; - u[0].setIConst(index); - - TType type(EbtInt, EbpUndefined, EvqConst, 1); - TIntermConstantUnion *node = new TIntermConstantUnion(u, type); - return node; -} - -TIntermBinary *constructGLFragDataNode(int index) -{ - TIntermBinary *indexDirect = new TIntermBinary(EOpIndexDirect); - TIntermSymbol *symbol = new TIntermSymbol(0, "gl_FragData", TType(EbtFloat, 4)); - indexDirect->setLeft(symbol); - TIntermConstantUnion *indexNode = constructIndexNode(index); - indexDirect->setRight(indexNode); - return indexDirect; -} - -TIntermBinary *constructGLFragDataAssignNode(int index) -{ - TIntermBinary *assign = new TIntermBinary(EOpAssign); - assign->setLeft(constructGLFragDataNode(index)); - assign->setRight(constructGLFragDataNode(0)); - assign->setType(TType(EbtFloat, 4)); - return assign; -} - class GLFragColorBroadcastTraverser : public TIntermTraverser { public: - GLFragColorBroadcastTraverser() - : TIntermTraverser(true, false, false), mMainSequence(nullptr), mGLFragColorUsed(false) + GLFragColorBroadcastTraverser(int maxDrawBuffers) + : TIntermTraverser(true, false, false), + mMainSequence(nullptr), + mGLFragColorUsed(false), + mMaxDrawBuffers(maxDrawBuffers) { } - void broadcastGLFragColor(int maxDrawBuffers); + void broadcastGLFragColor(); bool isGLFragColorUsed() const { return mGLFragColorUsed; } protected: void visitSymbol(TIntermSymbol *node) override; bool visitAggregate(Visit visit, TIntermAggregate *node) override; + TIntermBinary *constructGLFragDataNode(int index) const; + TIntermBinary *constructGLFragDataAssignNode(int index) const; + private: TIntermSequence *mMainSequence; bool mGLFragColorUsed; + int mMaxDrawBuffers; }; +TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataNode(int index) const +{ + TType gl_FragDataType = TType(EbtFloat, EbpMedium, EvqFragData, 4); + gl_FragDataType.setArraySize(mMaxDrawBuffers); + + TIntermSymbol *symbol = new TIntermSymbol(0, "gl_FragData", gl_FragDataType); + TIntermTyped *indexNode = TIntermTyped::CreateIndexNode(index); + + TIntermBinary *binary = new TIntermBinary(EOpIndexDirect, symbol, indexNode); + return binary; +} + +TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataAssignNode(int index) const +{ + TIntermTyped *fragDataIndex = constructGLFragDataNode(index); + TIntermTyped *fragDataZero = constructGLFragDataNode(0); + + return new TIntermBinary(EOpAssign, fragDataIndex, fragDataZero); +} + void GLFragColorBroadcastTraverser::visitSymbol(TIntermSymbol *node) { if (node->getSymbol() == "gl_FragColor") { queueReplacement(node, constructGLFragDataNode(0), OriginalNode::IS_DROPPED); mGLFragColorUsed = true; } } @@ -96,47 +94,47 @@ bool GLFragColorBroadcastTraverser::visi } break; default: break; } return true; } -void GLFragColorBroadcastTraverser::broadcastGLFragColor(int maxDrawBuffers) +void GLFragColorBroadcastTraverser::broadcastGLFragColor() { - ASSERT(maxDrawBuffers > 1); + ASSERT(mMaxDrawBuffers > 1); if (!mGLFragColorUsed) { return; } ASSERT(mMainSequence); // Now insert statements // gl_FragData[1] = gl_FragData[0]; // ... // gl_FragData[maxDrawBuffers - 1] = gl_FragData[0]; - for (int colorIndex = 1; colorIndex < maxDrawBuffers; ++colorIndex) + for (int colorIndex = 1; colorIndex < mMaxDrawBuffers; ++colorIndex) { mMainSequence->insert(mMainSequence->end(), constructGLFragDataAssignNode(colorIndex)); } } } // namespace anonymous void EmulateGLFragColorBroadcast(TIntermNode *root, int maxDrawBuffers, std::vector<sh::OutputVariable> *outputVariables) { ASSERT(maxDrawBuffers > 1); - GLFragColorBroadcastTraverser traverser; + GLFragColorBroadcastTraverser traverser(maxDrawBuffers); root->traverse(&traverser); if (traverser.isGLFragColorUsed()) { traverser.updateTree(); - traverser.broadcastGLFragColor(maxDrawBuffers); + traverser.broadcastGLFragColor(); for (auto &var : *outputVariables) { if (var.name == "gl_FragColor") { // TODO(zmo): Find a way to keep the original variable information. var.name = "gl_FragData"; var.mappedName = "gl_FragData"; var.arraySize = maxDrawBuffers;
--- a/gfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp +++ b/gfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp @@ -119,33 +119,28 @@ bool Traverser::visitAggregate(Visit vis TIntermAggregate *init = createTempInitDeclaration(lhs); TIntermTyped *current = createTempSymbol(lhs->getType()); insertStatementInParentBlock(init); // Create a chain of n-1 multiples. for (int i = 1; i < n; ++i) { - TIntermBinary *mul = new TIntermBinary(EOpMul); - mul->setLeft(current); - mul->setRight(createTempSymbol(lhs->getType())); - mul->setType(node->getType()); + TIntermBinary *mul = new TIntermBinary(EOpMul, current, createTempSymbol(lhs->getType())); mul->setLine(node->getLine()); current = mul; } // For negative pow, compute the reciprocal of the positive pow. if (exponent < 0) { TConstantUnion *oneVal = new TConstantUnion(); oneVal->setFConst(1.0f); TIntermConstantUnion *oneNode = new TIntermConstantUnion(oneVal, node->getType()); - TIntermBinary *div = new TIntermBinary(EOpDiv); - div->setLeft(oneNode); - div->setRight(current); + TIntermBinary *div = new TIntermBinary(EOpDiv, oneNode, current); current = div; } queueReplacement(node, current, OriginalNode::IS_DROPPED); mFound = true; return false; }
--- a/gfx/angle/src/compiler/translator/Initialize.cpp +++ b/gfx/angle/src/compiler/translator/Initialize.cpp @@ -506,26 +506,23 @@ void InsertBuiltInFunctions(sh::GLenum t symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxTextureImageUnits", resources.MaxTextureImageUnits, EbpMedium); symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxFragmentUniformVectors", resources.MaxFragmentUniformVectors, EbpMedium); symbolTable.insertConstInt(ESSL1_BUILTINS, "gl_MaxVaryingVectors", resources.MaxVaryingVectors, EbpMedium); - if (spec != SH_CSS_SHADERS_SPEC) + symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxDrawBuffers", resources.MaxDrawBuffers, + EbpMedium); + if (resources.EXT_blend_func_extended) { - symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxDrawBuffers", resources.MaxDrawBuffers, - EbpMedium); - if (resources.EXT_blend_func_extended) - { - symbolTable.insertConstIntExt(COMMON_BUILTINS, "GL_EXT_blend_func_extended", - "gl_MaxDualSourceDrawBuffersEXT", - resources.MaxDualSourceDrawBuffers); - } + symbolTable.insertConstIntExt(COMMON_BUILTINS, "GL_EXT_blend_func_extended", + "gl_MaxDualSourceDrawBuffersEXT", + resources.MaxDualSourceDrawBuffers); } symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxVertexOutputVectors", resources.MaxVertexOutputVectors, EbpMedium); symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxFragmentInputVectors", resources.MaxFragmentInputVectors, EbpMedium); symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MinProgramTexelOffset", resources.MinProgramTexelOffset, EbpMedium); @@ -585,95 +582,83 @@ void IdentifyBuiltIns(sh::GLenum type, S { // // Insert some special built-in variables that are not in // the built-in header files. // switch (type) { case GL_FRAGMENT_SHADER: - symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_FragCoord"), - TType(EbtFloat, EbpMedium, EvqFragCoord, 4))); - symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_FrontFacing"), - TType(EbtBool, EbpUndefined, EvqFrontFacing, 1))); - symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_PointCoord"), - TType(EbtFloat, EbpMedium, EvqPointCoord, 2))); + { + symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_FragCoord"), + TType(EbtFloat, EbpMedium, EvqFragCoord, 4))); + symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_FrontFacing"), + TType(EbtBool, EbpUndefined, EvqFrontFacing, 1))); + symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_PointCoord"), + TType(EbtFloat, EbpMedium, EvqPointCoord, 2))); - // - // In CSS Shaders, gl_FragColor, gl_FragData, and gl_MaxDrawBuffers are not available. - // Instead, css_MixColor and css_ColorMatrix are available. - // - if (spec != SH_CSS_SHADERS_SPEC) - { - symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragColor"), - TType(EbtFloat, EbpMedium, EvqFragColor, 4))); - TType fragData(EbtFloat, EbpMedium, EvqFragData, 4, 1, true); - fragData.setArraySize(resources.MaxDrawBuffers); - symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragData"), fragData)); + symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragColor"), + TType(EbtFloat, EbpMedium, EvqFragColor, 4))); + TType fragData(EbtFloat, EbpMedium, EvqFragData, 4, 1, true); + fragData.setArraySize(resources.MaxDrawBuffers); + symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragData"), fragData)); - if (resources.EXT_blend_func_extended) - { - symbolTable.insert( - ESSL1_BUILTINS, "GL_EXT_blend_func_extended", - new TVariable(NewPoolTString("gl_SecondaryFragColorEXT"), - TType(EbtFloat, EbpMedium, EvqSecondaryFragColorEXT, 4))); - TType secondaryFragData(EbtFloat, EbpMedium, EvqSecondaryFragDataEXT, 4, 1, true); - secondaryFragData.setArraySize(resources.MaxDualSourceDrawBuffers); - symbolTable.insert( - ESSL1_BUILTINS, "GL_EXT_blend_func_extended", - new TVariable(NewPoolTString("gl_SecondaryFragDataEXT"), secondaryFragData)); - } + if (resources.EXT_blend_func_extended) + { + symbolTable.insert( + ESSL1_BUILTINS, "GL_EXT_blend_func_extended", + new TVariable(NewPoolTString("gl_SecondaryFragColorEXT"), + TType(EbtFloat, EbpMedium, EvqSecondaryFragColorEXT, 4))); + TType secondaryFragData(EbtFloat, EbpMedium, EvqSecondaryFragDataEXT, 4, 1, true); + secondaryFragData.setArraySize(resources.MaxDualSourceDrawBuffers); + symbolTable.insert( + ESSL1_BUILTINS, "GL_EXT_blend_func_extended", + new TVariable(NewPoolTString("gl_SecondaryFragDataEXT"), secondaryFragData)); + } - if (resources.EXT_frag_depth) - { - symbolTable.insert( - ESSL1_BUILTINS, "GL_EXT_frag_depth", - new TVariable( - NewPoolTString("gl_FragDepthEXT"), - TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium, - EvqFragDepthEXT, 1))); - } + if (resources.EXT_frag_depth) + { + symbolTable.insert( + ESSL1_BUILTINS, "GL_EXT_frag_depth", + new TVariable( + NewPoolTString("gl_FragDepthEXT"), + TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium, + EvqFragDepthEXT, 1))); + } - symbolTable.insert(ESSL3_BUILTINS, - new TVariable(NewPoolTString("gl_FragDepth"), - TType(EbtFloat, EbpHigh, EvqFragDepth, 1))); + symbolTable.insert(ESSL3_BUILTINS, + new TVariable(NewPoolTString("gl_FragDepth"), + TType(EbtFloat, EbpHigh, EvqFragDepth, 1))); - if (resources.EXT_shader_framebuffer_fetch || resources.NV_shader_framebuffer_fetch) - { - TType lastFragData(EbtFloat, EbpMedium, EvqLastFragData, 4, 1, true); - lastFragData.setArraySize(resources.MaxDrawBuffers); + if (resources.EXT_shader_framebuffer_fetch || resources.NV_shader_framebuffer_fetch) + { + TType lastFragData(EbtFloat, EbpMedium, EvqLastFragData, 4, 1, true); + lastFragData.setArraySize(resources.MaxDrawBuffers); - if (resources.EXT_shader_framebuffer_fetch) - { - symbolTable.insert(ESSL1_BUILTINS, "GL_EXT_shader_framebuffer_fetch", - new TVariable(NewPoolTString("gl_LastFragData"), lastFragData)); - } - else if (resources.NV_shader_framebuffer_fetch) - { - symbolTable.insert(ESSL1_BUILTINS, "GL_NV_shader_framebuffer_fetch", - new TVariable(NewPoolTString("gl_LastFragColor"), - TType(EbtFloat, EbpMedium, EvqLastFragColor, 4))); - symbolTable.insert(ESSL1_BUILTINS, "GL_NV_shader_framebuffer_fetch", - new TVariable(NewPoolTString("gl_LastFragData"), lastFragData)); - } - } - else if (resources.ARM_shader_framebuffer_fetch) - { - symbolTable.insert(ESSL1_BUILTINS, "GL_ARM_shader_framebuffer_fetch", - new TVariable(NewPoolTString("gl_LastFragColorARM"), - TType(EbtFloat, EbpMedium, EvqLastFragColor, 4))); - } - } - else - { - symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("css_MixColor"), - TType(EbtFloat, EbpMedium, EvqGlobal, 4))); - symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("css_ColorMatrix"), - TType(EbtFloat, EbpMedium, EvqGlobal, 4, 4))); - } + if (resources.EXT_shader_framebuffer_fetch) + { + symbolTable.insert(ESSL1_BUILTINS, "GL_EXT_shader_framebuffer_fetch", + new TVariable(NewPoolTString("gl_LastFragData"), lastFragData)); + } + else if (resources.NV_shader_framebuffer_fetch) + { + symbolTable.insert(ESSL1_BUILTINS, "GL_NV_shader_framebuffer_fetch", + new TVariable(NewPoolTString("gl_LastFragColor"), + TType(EbtFloat, EbpMedium, EvqLastFragColor, 4))); + symbolTable.insert(ESSL1_BUILTINS, "GL_NV_shader_framebuffer_fetch", + new TVariable(NewPoolTString("gl_LastFragData"), lastFragData)); + } + } + else if (resources.ARM_shader_framebuffer_fetch) + { + symbolTable.insert(ESSL1_BUILTINS, "GL_ARM_shader_framebuffer_fetch", + new TVariable(NewPoolTString("gl_LastFragColorARM"), + TType(EbtFloat, EbpMedium, EvqLastFragColor, 4))); + } + } break; case GL_VERTEX_SHADER: symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_Position"), TType(EbtFloat, EbpHigh, EvqPosition, 4))); symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_PointSize"), TType(EbtFloat, EbpMedium, EvqPointSize, 1)));
--- a/gfx/angle/src/compiler/translator/InitializeVariables.cpp +++ b/gfx/angle/src/compiler/translator/InitializeVariables.cpp @@ -9,56 +9,16 @@ #include "angle_gl.h" #include "common/debug.h" #include "compiler/translator/IntermNode.h" #include "compiler/translator/util.h" namespace { -TIntermConstantUnion *constructConstUnionNode(const TType &type) -{ - TType myType = type; - myType.clearArrayness(); - myType.setQualifier(EvqConst); - size_t size = myType.getObjectSize(); - TConstantUnion *u = new TConstantUnion[size]; - for (size_t ii = 0; ii < size; ++ii) - { - switch (type.getBasicType()) - { - case EbtFloat: - u[ii].setFConst(0.0f); - break; - case EbtInt: - u[ii].setIConst(0); - break; - case EbtUInt: - u[ii].setUConst(0u); - break; - default: - UNREACHABLE(); - return nullptr; - } - } - - TIntermConstantUnion *node = new TIntermConstantUnion(u, myType); - return node; -} - -TIntermConstantUnion *constructIndexNode(int index) -{ - TConstantUnion *u = new TConstantUnion[1]; - u[0].setIConst(index); - - TType type(EbtInt, EbpUndefined, EvqConst, 1); - TIntermConstantUnion *node = new TIntermConstantUnion(u, type); - return node; -} - class VariableInitializer : public TIntermTraverser { public: VariableInitializer(const InitVariableList &vars) : TIntermTraverser(true, false, false), mVariables(vars), mCodeInserted(false) { } @@ -115,83 +75,53 @@ bool VariableInitializer::visitAggregate visitChildren = false; break; } return visitChildren; } void VariableInitializer::insertInitCode(TIntermSequence *sequence) { - for (size_t ii = 0; ii < mVariables.size(); ++ii) + for (const auto &var : mVariables) { - const sh::ShaderVariable &var = mVariables[ii]; TString name = TString(var.name.c_str()); + TType type = sh::GetShaderVariableType(var); + + // Assign the array elements one by one to keep the AST compatible with ESSL 1.00 which + // doesn't have array assignment. if (var.isArray()) { - TType type = sh::ConvertShaderVariableTypeToTType(var.type); size_t pos = name.find_last_of('['); if (pos != TString::npos) + { name = name.substr(0, pos); - for (int index = static_cast<int>(var.arraySize) - 1; index >= 0; --index) - { - TIntermBinary *assign = new TIntermBinary(EOpAssign); - sequence->insert(sequence->begin(), assign); - - TIntermBinary *indexDirect = new TIntermBinary(EOpIndexDirect); - TIntermSymbol *symbol = new TIntermSymbol(0, name, type); - indexDirect->setLeft(symbol); - TIntermConstantUnion *indexNode = constructIndexNode(index); - indexDirect->setRight(indexNode); - - assign->setLeft(indexDirect); - - TIntermConstantUnion *zeroConst = constructConstUnionNode(type); - assign->setRight(zeroConst); } - } - else if (var.isStruct()) - { - TFieldList *fields = new TFieldList; - TSourceLoc loc; - for (auto field : var.fields) - { - fields->push_back(new TField(nullptr, new TString(field.name.c_str()), loc)); - } - TStructure *structure = new TStructure(new TString(var.structName.c_str()), fields); - TType type; - type.setStruct(structure); - for (int fieldIndex = 0; fieldIndex < static_cast<int>(var.fields.size()); ++fieldIndex) + TType elementType = type; + elementType.clearArrayness(); + + for (unsigned int i = 0; i < var.arraySize; ++i) { - TIntermBinary *assign = new TIntermBinary(EOpAssign); - sequence->insert(sequence->begin(), assign); + TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, type); + TIntermBinary *element = new TIntermBinary(EOpIndexDirect, arraySymbol, + TIntermTyped::CreateIndexNode(i)); - TIntermBinary *indexDirectStruct = new TIntermBinary(EOpIndexDirectStruct); - TIntermSymbol *symbol = new TIntermSymbol(0, name, type); - indexDirectStruct->setLeft(symbol); - TIntermConstantUnion *indexNode = constructIndexNode(fieldIndex); - indexDirectStruct->setRight(indexNode); - assign->setLeft(indexDirectStruct); + TIntermTyped *zero = TIntermTyped::CreateZero(elementType); + TIntermBinary *assignment = new TIntermBinary(EOpAssign, element, zero); - const sh::ShaderVariable &field = var.fields[fieldIndex]; - TType fieldType = sh::ConvertShaderVariableTypeToTType(field.type); - TIntermConstantUnion *zeroConst = constructConstUnionNode(fieldType); - assign->setRight(zeroConst); + sequence->insert(sequence->begin(), assignment); } } else { - TType type = sh::ConvertShaderVariableTypeToTType(var.type); - TIntermBinary *assign = new TIntermBinary(EOpAssign); + TIntermSymbol *symbol = new TIntermSymbol(0, name, type); + TIntermTyped *zero = TIntermTyped::CreateZero(type); + + TIntermBinary *assign = new TIntermBinary(EOpAssign, symbol, zero); sequence->insert(sequence->begin(), assign); - TIntermSymbol *symbol = new TIntermSymbol(0, name, type); - assign->setLeft(symbol); - TIntermConstantUnion *zeroConst = constructConstUnionNode(type); - assign->setRight(zeroConst); } - } } } // namespace anonymous void InitializeVariables(TIntermNode *root, const InitVariableList &vars) { VariableInitializer initializer(vars);
--- a/gfx/angle/src/compiler/translator/InitializeVariables.h +++ b/gfx/angle/src/compiler/translator/InitializeVariables.h @@ -8,11 +8,12 @@ #define COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_ #include <GLSLANG/ShaderLang.h> class TIntermNode; typedef std::vector<sh::ShaderVariable> InitVariableList; +// This function cannot currently initialize structures containing arrays for an ESSL 1.00 backend. void InitializeVariables(TIntermNode *root, const InitVariableList &vars); #endif // COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
--- a/gfx/angle/src/compiler/translator/IntermNode.cpp +++ b/gfx/angle/src/compiler/translator/IntermNode.cpp @@ -16,16 +16,17 @@ #include <vector> #include "common/mathutil.h" #include "common/matrix_utils.h" #include "compiler/translator/Diagnostics.h" #include "compiler/translator/HashNames.h" #include "compiler/translator/IntermNode.h" #include "compiler/translator/SymbolTable.h" +#include "compiler/translator/util.h" namespace { const float kPi = 3.14159265358979323846f; const float kDegreesToRadiansMultiplier = kPi / 180.0f; const float kRadiansToDegreesMultiplier = 180.0f / kPi; @@ -38,23 +39,24 @@ TConstantUnion *Vectorize(const TConstan { TConstantUnion *constUnion = new TConstantUnion[size]; for (unsigned int i = 0; i < size; ++i) constUnion[i] = constant; return constUnion; } -void UndefinedConstantFoldingError(const TSourceLoc &loc, TOperator op, TBasicType basicType, - TInfoSink &infoSink, TConstantUnion *result) +void UndefinedConstantFoldingError(const TSourceLoc &loc, + TOperator op, + TBasicType basicType, + TDiagnostics *diagnostics, + TConstantUnion *result) { - std::stringstream constantFoldingErrorStream; - constantFoldingErrorStream << "'" << GetOperatorString(op) - << "' operation result is undefined for the values passed in"; - infoSink.info.message(EPrefixWarning, loc, constantFoldingErrorStream.str().c_str()); + diagnostics->warning(loc, "operation result is undefined for the values passed in", + GetOperatorString(op), ""); switch (basicType) { case EbtFloat : result->setFConst(0.0f); break; case EbtInt: result->setIConst(0); @@ -86,17 +88,17 @@ float VectorDotProduct(const TConstantUn size_t paramArraySize) { float result = 0.0f; for (size_t i = 0; i < paramArraySize; i++) result += paramArray1[i].getFConst() * paramArray2[i].getFConst(); return result; } -TIntermTyped *CreateFoldedNode(TConstantUnion *constArray, +TIntermTyped *CreateFoldedNode(const TConstantUnion *constArray, const TIntermTyped *originalNode, TQualifier qualifier) { if (constArray == nullptr) { return nullptr; } TIntermTyped *folded = new TIntermConstantUnion(constArray, originalNode->getType()); @@ -185,16 +187,17 @@ bool TIntermBinary::replaceChildNode( REPLACE_IF_IS(mLeft, TIntermTyped, original, replacement); REPLACE_IF_IS(mRight, TIntermTyped, original, replacement); return false; } bool TIntermUnary::replaceChildNode( TIntermNode *original, TIntermNode *replacement) { + ASSERT(original->getAsTyped()->getType() == replacement->getAsTyped()->getType()); REPLACE_IF_IS(mOperand, TIntermTyped, original, replacement); return false; } bool TIntermAggregate::replaceChildNode( TIntermNode *original, TIntermNode *replacement) { for (size_t ii = 0; ii < mSequence.size(); ++ii) @@ -284,16 +287,24 @@ void TIntermAggregate::setBuiltInFunctio // ESSL 3.0 spec section 8: textureSize always gets highp precision. // All other functions that take a sampler are assumed to be texture functions. if (mName.getString().find("textureSize") == 0) mType.setPrecision(EbpHigh); else mType.setPrecision(precision); } +bool TIntermTernary::replaceChildNode(TIntermNode *original, TIntermNode *replacement) +{ + REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement); + REPLACE_IF_IS(mTrueExpression, TIntermTyped, original, replacement); + REPLACE_IF_IS(mFalseExpression, TIntermTyped, original, replacement); + return false; +} + bool TIntermSelection::replaceChildNode( TIntermNode *original, TIntermNode *replacement) { REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement); REPLACE_IF_IS(mTrueBlock, TIntermNode, original, replacement); REPLACE_IF_IS(mFalseBlock, TIntermNode, original, replacement); return false; } @@ -331,16 +342,93 @@ bool TIntermTyped::isConstructorWithOnly for (TIntermNode *&node : *constructor->getSequence()) { if (!node->getAsConstantUnion()) return false; } return true; } +// static +TIntermTyped *TIntermTyped::CreateIndexNode(int index) +{ + TConstantUnion *u = new TConstantUnion[1]; + u[0].setIConst(index); + + TType type(EbtInt, EbpUndefined, EvqConst, 1); + TIntermConstantUnion *node = new TIntermConstantUnion(u, type); + return node; +} + +// static +TIntermTyped *TIntermTyped::CreateZero(const TType &type) +{ + TType constType(type); + constType.setQualifier(EvqConst); + + if (!type.isArray() && type.getBasicType() != EbtStruct) + { + ASSERT(type.isScalar() || type.isVector() || type.isMatrix()); + + size_t size = constType.getObjectSize(); + TConstantUnion *u = new TConstantUnion[size]; + for (size_t i = 0; i < size; ++i) + { + switch (type.getBasicType()) + { + case EbtFloat: + u[i].setFConst(0.0f); + break; + case EbtInt: + u[i].setIConst(0); + break; + case EbtUInt: + u[i].setUConst(0u); + break; + case EbtBool: + u[i].setBConst(false); + break; + default: + UNREACHABLE(); + return nullptr; + } + } + + TIntermConstantUnion *node = new TIntermConstantUnion(u, constType); + return node; + } + + TIntermAggregate *constructor = new TIntermAggregate(sh::TypeToConstructorOperator(type)); + constructor->setType(constType); + + if (type.isArray()) + { + TType elementType(type); + elementType.clearArrayness(); + + size_t arraySize = type.getArraySize(); + for (size_t i = 0; i < arraySize; ++i) + { + constructor->getSequence()->push_back(CreateZero(elementType)); + } + } + else + { + ASSERT(type.getBasicType() == EbtStruct); + + TStructure *structure = type.getStruct(); + for (const auto &field : structure->fields()) + { + constructor->getSequence()->push_back(CreateZero(*field->type())); + } + } + + return constructor; +} + TIntermConstantUnion::TIntermConstantUnion(const TIntermConstantUnion &node) : TIntermTyped(node) { mUnionArrayPointer = node.mUnionArrayPointer; } TIntermAggregate::TIntermAggregate(const TIntermAggregate &node) : TIntermOperator(node), mName(node.mName), @@ -371,30 +459,25 @@ TIntermBinary::TIntermBinary(const TInte TIntermUnary::TIntermUnary(const TIntermUnary &node) : TIntermOperator(node), mUseEmulatedFunction(node.mUseEmulatedFunction) { TIntermTyped *operandCopy = node.mOperand->deepCopy(); ASSERT(operandCopy != nullptr); mOperand = operandCopy; } -TIntermSelection::TIntermSelection(const TIntermSelection &node) : TIntermTyped(node) +TIntermTernary::TIntermTernary(const TIntermTernary &node) : TIntermTyped(node) { - // Only supported for ternary nodes, not if statements. - TIntermTyped *trueTyped = node.mTrueBlock->getAsTyped(); - TIntermTyped *falseTyped = node.mFalseBlock->getAsTyped(); - ASSERT(trueTyped != nullptr); - ASSERT(falseTyped != nullptr); TIntermTyped *conditionCopy = node.mCondition->deepCopy(); - TIntermTyped *trueCopy = trueTyped->deepCopy(); - TIntermTyped *falseCopy = falseTyped->deepCopy(); + TIntermTyped *trueCopy = node.mTrueExpression->deepCopy(); + TIntermTyped *falseCopy = node.mFalseExpression->deepCopy(); ASSERT(conditionCopy != nullptr && trueCopy != nullptr && falseCopy != nullptr); - mCondition = conditionCopy; - mTrueBlock = trueCopy; - mFalseBlock = falseCopy; + mCondition = conditionCopy; + mTrueExpression = trueCopy; + mFalseExpression = falseCopy; } bool TIntermOperator::isAssignment() const { return IsAssignment(mOp); } bool TIntermOperator::isMultiplication() const @@ -539,96 +622,190 @@ TOperator TIntermBinary::GetMulAssignOpB } } } // // Make sure the type of a unary operator is appropriate for its // combination of operation and operand type. // -void TIntermUnary::promote(const TType *funcReturnType) +void TIntermUnary::promote() { + TQualifier resultQualifier = EvqTemporary; + if (mOperand->getQualifier() == EvqConst) + resultQualifier = EvqConst; + + unsigned char operandPrimarySize = + static_cast<unsigned char>(mOperand->getType().getNominalSize()); switch (mOp) { - case EOpFloatBitsToInt: - case EOpFloatBitsToUint: - case EOpIntBitsToFloat: - case EOpUintBitsToFloat: - case EOpPackSnorm2x16: - case EOpPackUnorm2x16: - case EOpPackHalf2x16: - case EOpUnpackSnorm2x16: - case EOpUnpackUnorm2x16: - mType.setPrecision(EbpHigh); - break; - case EOpUnpackHalf2x16: - mType.setPrecision(EbpMedium); - break; - default: - setType(mOperand->getType()); + case EOpFloatBitsToInt: + setType(TType(EbtInt, EbpHigh, resultQualifier, operandPrimarySize)); + break; + case EOpFloatBitsToUint: + setType(TType(EbtUInt, EbpHigh, resultQualifier, operandPrimarySize)); + break; + case EOpIntBitsToFloat: + case EOpUintBitsToFloat: + setType(TType(EbtFloat, EbpHigh, resultQualifier, operandPrimarySize)); + break; + case EOpPackSnorm2x16: + case EOpPackUnorm2x16: + case EOpPackHalf2x16: + setType(TType(EbtUInt, EbpHigh, resultQualifier)); + break; + case EOpUnpackSnorm2x16: + case EOpUnpackUnorm2x16: + setType(TType(EbtFloat, EbpHigh, resultQualifier, 2)); + break; + case EOpUnpackHalf2x16: + setType(TType(EbtFloat, EbpMedium, resultQualifier, 2)); + break; + case EOpAny: + case EOpAll: + setType(TType(EbtBool, EbpUndefined, resultQualifier)); + break; + case EOpLength: + case EOpDeterminant: + setType(TType(EbtFloat, mOperand->getType().getPrecision(), resultQualifier)); + break; + case EOpTranspose: + setType(TType(EbtFloat, mOperand->getType().getPrecision(), resultQualifier, + static_cast<unsigned char>(mOperand->getType().getRows()), + static_cast<unsigned char>(mOperand->getType().getCols()))); + break; + case EOpIsInf: + case EOpIsNan: + setType(TType(EbtBool, EbpUndefined, resultQualifier, operandPrimarySize)); + break; + default: + setType(mOperand->getType()); + mType.setQualifier(resultQualifier); + break; } +} - if (funcReturnType != nullptr) - { - if (funcReturnType->getBasicType() == EbtBool) - { - // Bool types should not have precision. - setType(*funcReturnType); - } - else - { - // Precision of the node has been set based on the operand. - setTypePreservePrecision(*funcReturnType); - } - } - - if (mOperand->getQualifier() == EvqConst) - mType.setQualifier(EvqConst); - else - mType.setQualifier(EvqTemporary); +TIntermUnary::TIntermUnary(TOperator op, TIntermTyped *operand) + : TIntermOperator(op), mOperand(operand), mUseEmulatedFunction(false) +{ + promote(); } TIntermBinary::TIntermBinary(TOperator op, TIntermTyped *left, TIntermTyped *right) : TIntermOperator(op), mLeft(left), mRight(right), mAddIndexClamp(false) { promote(); } +TIntermTernary::TIntermTernary(TIntermTyped *cond, + TIntermTyped *trueExpression, + TIntermTyped *falseExpression) + : TIntermTyped(trueExpression->getType()), + mCondition(cond), + mTrueExpression(trueExpression), + mFalseExpression(falseExpression) +{ + getTypePointer()->setQualifier( + TIntermTernary::DetermineQualifier(cond, trueExpression, falseExpression)); +} + +// static +TQualifier TIntermTernary::DetermineQualifier(TIntermTyped *cond, + TIntermTyped *trueExpression, + TIntermTyped *falseExpression) +{ + if (cond->getQualifier() == EvqConst && trueExpression->getQualifier() == EvqConst && + falseExpression->getQualifier() == EvqConst) + { + return EvqConst; + } + return EvqTemporary; +} + // // Establishes the type of the resultant operation, as well as // makes the operator the correct one for the operands. // // For lots of operations it should already be established that the operand // combination is valid, but returns false if operator can't work on operands. // void TIntermBinary::promote() { - ASSERT(mLeft->isArray() == mRight->isArray()); - ASSERT(!isMultiplication() || mOp == GetMulOpBasedOnOperands(mLeft->getType(), mRight->getType())); // Base assumption: just make the type the same as the left // operand. Then only deviations from this need be coded. setType(mLeft->getType()); - // The result gets promoted to the highest precision. - TPrecision higherPrecision = GetHigherPrecision( - mLeft->getPrecision(), mRight->getPrecision()); - getTypePointer()->setPrecision(higherPrecision); - TQualifier resultQualifier = EvqConst; // Binary operations results in temporary variables unless both // operands are const. if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst) { resultQualifier = EvqTemporary; getTypePointer()->setQualifier(EvqTemporary); } + // Handle indexing ops. + switch (mOp) + { + case EOpIndexDirect: + case EOpIndexIndirect: + if (mLeft->isArray()) + { + mType.clearArrayness(); + } + else if (mLeft->isMatrix()) + { + setType(TType(mLeft->getBasicType(), mLeft->getPrecision(), resultQualifier, + static_cast<unsigned char>(mLeft->getRows()))); + } + else if (mLeft->isVector()) + { + setType(TType(mLeft->getBasicType(), mLeft->getPrecision(), resultQualifier)); + } + else + { + UNREACHABLE(); + } + return; + case EOpIndexDirectStruct: + { + const TFieldList &fields = mLeft->getType().getStruct()->fields(); + const int i = mRight->getAsConstantUnion()->getIConst(0); + setType(*fields[i]->type()); + getTypePointer()->setQualifier(resultQualifier); + return; + } + case EOpIndexDirectInterfaceBlock: + { + const TFieldList &fields = mLeft->getType().getInterfaceBlock()->fields(); + const int i = mRight->getAsConstantUnion()->getIConst(0); + setType(*fields[i]->type()); + getTypePointer()->setQualifier(resultQualifier); + return; + } + case EOpVectorSwizzle: + { + auto numFields = mRight->getAsAggregate()->getSequence()->size(); + setType(TType(mLeft->getBasicType(), mLeft->getPrecision(), resultQualifier, + static_cast<unsigned char>(numFields))); + return; + } + default: + break; + } + + ASSERT(mLeft->isArray() == mRight->isArray()); + + // The result gets promoted to the highest precision. + TPrecision higherPrecision = GetHigherPrecision(mLeft->getPrecision(), mRight->getPrecision()); + getTypePointer()->setPrecision(higherPrecision); + const int nominalSize = std::max(mLeft->getNominalSize(), mRight->getNominalSize()); // // All scalars or structs. Code after this test assumes this case is removed! // if (nominalSize == 1) { @@ -638,27 +815,27 @@ void TIntermBinary::promote() // Promote to conditional // case EOpEqual: case EOpNotEqual: case EOpLessThan: case EOpGreaterThan: case EOpLessThanEqual: case EOpGreaterThanEqual: - setType(TType(EbtBool, EbpUndefined)); - break; + setType(TType(EbtBool, EbpUndefined, resultQualifier)); + break; // // And and Or operate on conditionals // case EOpLogicalAnd: case EOpLogicalXor: case EOpLogicalOr: ASSERT(mLeft->getBasicType() == EbtBool && mRight->getBasicType() == EbtBool); - setType(TType(EbtBool, EbpUndefined)); + setType(TType(EbtBool, EbpUndefined, resultQualifier)); break; default: break; } return; } @@ -744,46 +921,126 @@ void TIntermBinary::promote() (mLeft->getSecondarySize() == mRight->getSecondarySize())); setType(TType(EbtBool, EbpUndefined, resultQualifier)); break; case EOpIndexDirect: case EOpIndexIndirect: case EOpIndexDirectInterfaceBlock: case EOpIndexDirectStruct: - // TODO (oetuaho): These ops could be handled here as well (should be done closer to the - // top of the function). + case EOpVectorSwizzle: + // These ops should be already fully handled. UNREACHABLE(); break; default: UNREACHABLE(); break; } } -TIntermTyped *TIntermBinary::fold(TDiagnostics *diagnostics) +const TConstantUnion *TIntermConstantUnion::foldIndexing(int index) { - TIntermConstantUnion *leftConstant = mLeft->getAsConstantUnion(); - TIntermConstantUnion *rightConstant = mRight->getAsConstantUnion(); - if (leftConstant == nullptr || rightConstant == nullptr) + if (isArray()) + { + ASSERT(index < static_cast<int>(getType().getArraySize())); + TType arrayElementType = getType(); + arrayElementType.clearArrayness(); + size_t arrayElementSize = arrayElementType.getObjectSize(); + return &mUnionArrayPointer[arrayElementSize * index]; + } + else if (isMatrix()) { + ASSERT(index < getType().getCols()); + int size = getType().getRows(); + return &mUnionArrayPointer[size * index]; + } + else if (isVector()) + { + ASSERT(index < getType().getNominalSize()); + return &mUnionArrayPointer[index]; + } + else + { + UNREACHABLE(); return nullptr; } - TConstantUnion *constArray = leftConstant->foldBinary(mOp, rightConstant, diagnostics); - - // Nodes may be constant folded without being qualified as constant. - TQualifier resultQualifier = EvqConst; - if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst) - { - resultQualifier = EvqTemporary; - } - return CreateFoldedNode(constArray, this, resultQualifier); } -TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink) +TIntermTyped *TIntermBinary::fold(TDiagnostics *diagnostics) +{ + TIntermConstantUnion *leftConstant = mLeft->getAsConstantUnion(); + TIntermConstantUnion *rightConstant = mRight->getAsConstantUnion(); + switch (mOp) + { + case EOpIndexDirect: + { + if (leftConstant == nullptr || rightConstant == nullptr) + { + return nullptr; + } + int index = rightConstant->getIConst(0); + + const TConstantUnion *constArray = leftConstant->foldIndexing(index); + return CreateFoldedNode(constArray, this, mType.getQualifier()); + } + case EOpIndexDirectStruct: + { + if (leftConstant == nullptr || rightConstant == nullptr) + { + return nullptr; + } + const TFieldList &fields = mLeft->getType().getStruct()->fields(); + size_t index = static_cast<size_t>(rightConstant->getIConst(0)); + + size_t previousFieldsSize = 0; + for (size_t i = 0; i < index; ++i) + { + previousFieldsSize += fields[i]->type()->getObjectSize(); + } + + const TConstantUnion *constArray = leftConstant->getUnionArrayPointer(); + return CreateFoldedNode(constArray + previousFieldsSize, this, mType.getQualifier()); + } + case EOpIndexIndirect: + case EOpIndexDirectInterfaceBlock: + // Can never be constant folded. + return nullptr; + case EOpVectorSwizzle: + { + if (leftConstant == nullptr) + { + return nullptr; + } + TIntermAggregate *fieldsAgg = mRight->getAsAggregate(); + TIntermSequence *fieldsSequence = fieldsAgg->getSequence(); + size_t numFields = fieldsSequence->size(); + + TConstantUnion *constArray = new TConstantUnion[numFields]; + for (size_t i = 0; i < numFields; i++) + { + int fieldOffset = fieldsSequence->at(i)->getAsConstantUnion()->getIConst(0); + constArray[i] = *leftConstant->foldIndexing(fieldOffset); + } + return CreateFoldedNode(constArray, this, mType.getQualifier()); + } + default: + { + if (leftConstant == nullptr || rightConstant == nullptr) + { + return nullptr; + } + TConstantUnion *constArray = leftConstant->foldBinary(mOp, rightConstant, diagnostics); + + // Nodes may be constant folded without being qualified as constant. + return CreateFoldedNode(constArray, this, mType.getQualifier()); + } + } +} + +TIntermTyped *TIntermUnary::fold(TDiagnostics *diagnostics) { TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion(); if (operandConstant == nullptr) { return nullptr; } TConstantUnion *constArray = nullptr; @@ -796,43 +1053,42 @@ TIntermTyped *TIntermUnary::fold(TInfoSi case EOpDeterminant: case EOpInverse: case EOpPackSnorm2x16: case EOpUnpackSnorm2x16: case EOpPackUnorm2x16: case EOpUnpackUnorm2x16: case EOpPackHalf2x16: case EOpUnpackHalf2x16: - constArray = operandConstant->foldUnaryWithDifferentReturnType(mOp, infoSink); - break; + constArray = operandConstant->foldUnaryNonComponentWise(mOp); + break; default: - constArray = operandConstant->foldUnaryWithSameReturnType(mOp, infoSink); - break; + constArray = operandConstant->foldUnaryComponentWise(mOp, diagnostics); + break; } // Nodes may be constant folded without being qualified as constant. - TQualifier resultQualifier = mOperand->getQualifier() == EvqConst ? EvqConst : EvqTemporary; - return CreateFoldedNode(constArray, this, resultQualifier); + return CreateFoldedNode(constArray, this, mType.getQualifier()); } -TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink) +TIntermTyped *TIntermAggregate::fold(TDiagnostics *diagnostics) { // Make sure that all params are constant before actual constant folding. for (auto *param : *getSequence()) { if (param->getAsConstantUnion() == nullptr) { return nullptr; } } TConstantUnion *constArray = nullptr; if (isConstructor()) - constArray = TIntermConstantUnion::FoldAggregateConstructor(this, infoSink); + constArray = TIntermConstantUnion::FoldAggregateConstructor(this); else - constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, infoSink); + constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, diagnostics); // Nodes may be constant folded without being qualified as constant. TQualifier resultQualifier = areChildrenConstQualified() ? EvqConst : EvqTemporary; return CreateFoldedNode(constArray, this, resultQualifier); } // // The fold functions see if an operation on a constant can be done in place, @@ -842,20 +1098,17 @@ TIntermTyped *TIntermAggregate::fold(TIn // TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUnion *rightNode, TDiagnostics *diagnostics) { const TConstantUnion *leftArray = getUnionArrayPointer(); const TConstantUnion *rightArray = rightNode->getUnionArrayPointer(); - if (!leftArray) - return nullptr; - if (!rightArray) - return nullptr; + ASSERT(leftArray && rightArray); size_t objectSize = getType().getObjectSize(); // for a case like float f = vec4(2, 3, 4, 5) + 1.2; if (rightNode->getType().getObjectSize() == 1 && objectSize > 1) { rightArray = Vectorize(*rightNode->getUnionArrayPointer(), objectSize); } @@ -868,30 +1121,30 @@ TConstantUnion *TIntermConstantUnion::fo TConstantUnion *resultArray = nullptr; switch(op) { case EOpAdd: resultArray = new TConstantUnion[objectSize]; for (size_t i = 0; i < objectSize; i++) - resultArray[i] = leftArray[i] + rightArray[i]; + resultArray[i] = TConstantUnion::add(leftArray[i], rightArray[i], diagnostics); break; case EOpSub: resultArray = new TConstantUnion[objectSize]; for (size_t i = 0; i < objectSize; i++) - resultArray[i] = leftArray[i] - rightArray[i]; + resultArray[i] = TConstantUnion::sub(leftArray[i], rightArray[i], diagnostics); break; case EOpMul: case EOpVectorTimesScalar: case EOpMatrixTimesScalar: resultArray = new TConstantUnion[objectSize]; for (size_t i = 0; i < objectSize; i++) - resultArray[i] = leftArray[i] * rightArray[i]; + resultArray[i] = TConstantUnion::mul(leftArray[i], rightArray[i], diagnostics); break; case EOpMatrixTimesMatrix: { ASSERT(getType().getBasicType() == EbtFloat && rightNode->getBasicType() == EbtFloat); const int leftCols = getCols(); const int leftRows = getRows(); @@ -1142,690 +1395,539 @@ TConstantUnion *TIntermConstantUnion::fo default: UNREACHABLE(); return nullptr; } return resultArray; } -// -// The fold functions see if an operation on a constant can be done in place, -// without generating run-time code. -// -// Returns the constant value to keep using or nullptr. -// -TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink) +// The fold functions do operations on a constant at GLSL compile time, without generating run-time +// code. Returns the constant value to keep using. Nullptr should not be returned. +TConstantUnion *TIntermConstantUnion::foldUnaryNonComponentWise(TOperator op) { - // - // Do operations where the return type has a different number of components compared to the operand type. - // + // Do operations where the return type may have a different number of components compared to the + // operand type. const TConstantUnion *operandArray = getUnionArrayPointer(); - if (!operandArray) - return nullptr; + ASSERT(operandArray); size_t objectSize = getType().getObjectSize(); TConstantUnion *resultArray = nullptr; switch (op) { - case EOpAny: - if (getType().getBasicType() == EbtBool) - { + case EOpAny: + ASSERT(getType().getBasicType() == EbtBool); resultArray = new TConstantUnion(); resultArray->setBConst(false); for (size_t i = 0; i < objectSize; i++) { if (operandArray[i].getBConst()) { resultArray->setBConst(true); break; } } break; - } - else - { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - } - case EOpAll: - if (getType().getBasicType() == EbtBool) - { + case EOpAll: + ASSERT(getType().getBasicType() == EbtBool); resultArray = new TConstantUnion(); resultArray->setBConst(true); for (size_t i = 0; i < objectSize; i++) { if (!operandArray[i].getBConst()) { resultArray->setBConst(false); break; } } break; - } - else - { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - } - case EOpLength: - if (getType().getBasicType() == EbtFloat) - { + case EOpLength: + ASSERT(getType().getBasicType() == EbtFloat); resultArray = new TConstantUnion(); resultArray->setFConst(VectorLength(operandArray, objectSize)); break; - } - else + + case EOpTranspose: { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - } - - case EOpTranspose: - if (getType().getBasicType() == EbtFloat) - { + ASSERT(getType().getBasicType() == EbtFloat); resultArray = new TConstantUnion[objectSize]; angle::Matrix<float> result = GetMatrix(operandArray, getType().getRows(), getType().getCols()).transpose(); SetUnionArrayFromMatrix(result, resultArray); break; } - else + + case EOpDeterminant: { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - } - - case EOpDeterminant: - if (getType().getBasicType() == EbtFloat) - { + ASSERT(getType().getBasicType() == EbtFloat); unsigned int size = getType().getNominalSize(); ASSERT(size >= 2 && size <= 4); resultArray = new TConstantUnion(); resultArray->setFConst(GetMatrix(operandArray, size).determinant()); break; } - else + + case EOpInverse: { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - } - - case EOpInverse: - if (getType().getBasicType() == EbtFloat) - { + ASSERT(getType().getBasicType() == EbtFloat); unsigned int size = getType().getNominalSize(); ASSERT(size >= 2 && size <= 4); - resultArray = new TConstantUnion[objectSize]; + resultArray = new TConstantUnion[objectSize]; angle::Matrix<float> result = GetMatrix(operandArray, size).inverse(); SetUnionArrayFromMatrix(result, resultArray); break; } - else - { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - } - case EOpPackSnorm2x16: - if (getType().getBasicType() == EbtFloat) - { + case EOpPackSnorm2x16: + ASSERT(getType().getBasicType() == EbtFloat); ASSERT(getType().getNominalSize() == 2); resultArray = new TConstantUnion(); - resultArray->setUConst(gl::packSnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); + resultArray->setUConst( + gl::packSnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); break; - } - else + + case EOpUnpackSnorm2x16: { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - } - - case EOpUnpackSnorm2x16: - if (getType().getBasicType() == EbtUInt) - { + ASSERT(getType().getBasicType() == EbtUInt); resultArray = new TConstantUnion[2]; float f1, f2; gl::unpackSnorm2x16(operandArray[0].getUConst(), &f1, &f2); resultArray[0].setFConst(f1); resultArray[1].setFConst(f2); break; } - else - { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - } - case EOpPackUnorm2x16: - if (getType().getBasicType() == EbtFloat) - { + case EOpPackUnorm2x16: + ASSERT(getType().getBasicType() == EbtFloat); ASSERT(getType().getNominalSize() == 2); resultArray = new TConstantUnion(); - resultArray->setUConst(gl::packUnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); + resultArray->setUConst( + gl::packUnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); break; - } - else + + case EOpUnpackUnorm2x16: { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - } - - case EOpUnpackUnorm2x16: - if (getType().getBasicType() == EbtUInt) - { + ASSERT(getType().getBasicType() == EbtUInt); resultArray = new TConstantUnion[2]; float f1, f2; gl::unpackUnorm2x16(operandArray[0].getUConst(), &f1, &f2); resultArray[0].setFConst(f1); resultArray[1].setFConst(f2); break; } - else - { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - } - case EOpPackHalf2x16: - if (getType().getBasicType() == EbtFloat) - { + case EOpPackHalf2x16: + ASSERT(getType().getBasicType() == EbtFloat); ASSERT(getType().getNominalSize() == 2); resultArray = new TConstantUnion(); - resultArray->setUConst(gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); + resultArray->setUConst( + gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); break; - } - else + + case EOpUnpackHalf2x16: { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - } - - case EOpUnpackHalf2x16: - if (getType().getBasicType() == EbtUInt) - { + ASSERT(getType().getBasicType() == EbtUInt); resultArray = new TConstantUnion[2]; float f1, f2; gl::unpackHalf2x16(operandArray[0].getUConst(), &f1, &f2); resultArray[0].setFConst(f1); resultArray[1].setFConst(f2); break; } - else - { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - } - break; - default: - break; + default: + UNREACHABLE(); + break; } return resultArray; } -TConstantUnion *TIntermConstantUnion::foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink) +TConstantUnion *TIntermConstantUnion::foldUnaryComponentWise(TOperator op, + TDiagnostics *diagnostics) { - // - // Do unary operations where the return type is the same as operand type. - // + // Do unary operations where each component of the result is computed based on the corresponding + // component of the operand. Also folds normalize, though the divisor in that case takes all + // components into account. const TConstantUnion *operandArray = getUnionArrayPointer(); - if (!operandArray) - return nullptr; + ASSERT(operandArray); size_t objectSize = getType().getObjectSize(); TConstantUnion *resultArray = new TConstantUnion[objectSize]; for (size_t i = 0; i < objectSize; i++) { switch(op) { - case EOpNegative: - switch (getType().getBasicType()) - { - case EbtFloat: - resultArray[i].setFConst(-operandArray[i].getFConst()); - break; - case EbtInt: - resultArray[i].setIConst(-operandArray[i].getIConst()); - break; - case EbtUInt: - resultArray[i].setUConst(static_cast<unsigned int>( - -static_cast<int>(operandArray[i].getUConst()))); - break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return nullptr; - } - break; - - case EOpPositive: - switch (getType().getBasicType()) - { - case EbtFloat: - resultArray[i].setFConst(operandArray[i].getFConst()); - break; - case EbtInt: - resultArray[i].setIConst(operandArray[i].getIConst()); - break; - case EbtUInt: - resultArray[i].setUConst(static_cast<unsigned int>( - static_cast<int>(operandArray[i].getUConst()))); + case EOpNegative: + switch (getType().getBasicType()) + { + case EbtFloat: + resultArray[i].setFConst(-operandArray[i].getFConst()); + break; + case EbtInt: + resultArray[i].setIConst(-operandArray[i].getIConst()); + break; + case EbtUInt: + resultArray[i].setUConst(static_cast<unsigned int>( + -static_cast<int>(operandArray[i].getUConst()))); + break; + default: + UNREACHABLE(); + return nullptr; + } break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return nullptr; - } - break; - case EOpLogicalNot: - // this code is written for possible future use, - // will not get executed currently - switch (getType().getBasicType()) - { - case EbtBool: - resultArray[i].setBConst(!operandArray[i].getBConst()); + case EOpPositive: + switch (getType().getBasicType()) + { + case EbtFloat: + resultArray[i].setFConst(operandArray[i].getFConst()); + break; + case EbtInt: + resultArray[i].setIConst(operandArray[i].getIConst()); + break; + case EbtUInt: + resultArray[i].setUConst(static_cast<unsigned int>( + static_cast<int>(operandArray[i].getUConst()))); + break; + default: + UNREACHABLE(); + return nullptr; + } break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return nullptr; - } - break; - case EOpBitwiseNot: - switch (getType().getBasicType()) - { - case EbtInt: - resultArray[i].setIConst(~operandArray[i].getIConst()); + case EOpLogicalNot: + switch (getType().getBasicType()) + { + case EbtBool: + resultArray[i].setBConst(!operandArray[i].getBConst()); + break; + default: + UNREACHABLE(); + return nullptr; + } break; - case EbtUInt: - resultArray[i].setUConst(~operandArray[i].getUConst()); + + case EOpBitwiseNot: + switch (getType().getBasicType()) + { + case EbtInt: + resultArray[i].setIConst(~operandArray[i].getIConst()); + break; + case EbtUInt: + resultArray[i].setUConst(~operandArray[i].getUConst()); + break; + default: + UNREACHABLE(); + return nullptr; + } break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return nullptr; - } - break; - case EOpRadians: - if (getType().getBasicType() == EbtFloat) - { + case EOpRadians: + ASSERT(getType().getBasicType() == EbtFloat); resultArray[i].setFConst(kDegreesToRadiansMultiplier * operandArray[i].getFConst()); break; - } - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return nullptr; - case EOpDegrees: - if (getType().getBasicType() == EbtFloat) - { + case EOpDegrees: + ASSERT(getType().getBasicType() == EbtFloat); resultArray[i].setFConst(kRadiansToDegreesMultiplier * operandArray[i].getFConst()); break; - } - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return nullptr; + + case EOpSin: + foldFloatTypeUnary(operandArray[i], &sinf, &resultArray[i]); + break; - case EOpSin: - if (!foldFloatTypeUnary(operandArray[i], &sinf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpCos: + foldFloatTypeUnary(operandArray[i], &cosf, &resultArray[i]); + break; - case EOpCos: - if (!foldFloatTypeUnary(operandArray[i], &cosf, infoSink, &resultArray[i])) - return nullptr; - break; - - case EOpTan: - if (!foldFloatTypeUnary(operandArray[i], &tanf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpTan: + foldFloatTypeUnary(operandArray[i], &tanf, &resultArray[i]); + break; - case EOpAsin: - // For asin(x), results are undefined if |x| > 1, we are choosing to set result to 0. - if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f) - UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); - else if (!foldFloatTypeUnary(operandArray[i], &asinf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpAsin: + // For asin(x), results are undefined if |x| > 1, we are choosing to set result to + // 0. + if (fabsf(operandArray[i].getFConst()) > 1.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), + diagnostics, &resultArray[i]); + else + foldFloatTypeUnary(operandArray[i], &asinf, &resultArray[i]); + break; - case EOpAcos: - // For acos(x), results are undefined if |x| > 1, we are choosing to set result to 0. - if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f) - UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); - else if (!foldFloatTypeUnary(operandArray[i], &acosf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpAcos: + // For acos(x), results are undefined if |x| > 1, we are choosing to set result to + // 0. + if (fabsf(operandArray[i].getFConst()) > 1.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), + diagnostics, &resultArray[i]); + else + foldFloatTypeUnary(operandArray[i], &acosf, &resultArray[i]); + break; - case EOpAtan: - if (!foldFloatTypeUnary(operandArray[i], &atanf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpAtan: + foldFloatTypeUnary(operandArray[i], &atanf, &resultArray[i]); + break; - case EOpSinh: - if (!foldFloatTypeUnary(operandArray[i], &sinhf, infoSink, &resultArray[i])) - return nullptr; - break; - - case EOpCosh: - if (!foldFloatTypeUnary(operandArray[i], &coshf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpSinh: + foldFloatTypeUnary(operandArray[i], &sinhf, &resultArray[i]); + break; - case EOpTanh: - if (!foldFloatTypeUnary(operandArray[i], &tanhf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpCosh: + foldFloatTypeUnary(operandArray[i], &coshf, &resultArray[i]); + break; - case EOpAsinh: - if (!foldFloatTypeUnary(operandArray[i], &asinhf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpTanh: + foldFloatTypeUnary(operandArray[i], &tanhf, &resultArray[i]); + break; - case EOpAcosh: - // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0. - if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 1.0f) - UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); - else if (!foldFloatTypeUnary(operandArray[i], &acoshf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpAsinh: + foldFloatTypeUnary(operandArray[i], &asinhf, &resultArray[i]); + break; - case EOpAtanh: - // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to 0. - if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) >= 1.0f) - UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); - else if (!foldFloatTypeUnary(operandArray[i], &atanhf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpAcosh: + // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0. + if (operandArray[i].getFConst() < 1.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), + diagnostics, &resultArray[i]); + else + foldFloatTypeUnary(operandArray[i], &acoshf, &resultArray[i]); + break; - case EOpAbs: - switch (getType().getBasicType()) - { - case EbtFloat: - resultArray[i].setFConst(fabsf(operandArray[i].getFConst())); - break; - case EbtInt: - resultArray[i].setIConst(abs(operandArray[i].getIConst())); + case EOpAtanh: + // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to + // 0. + if (fabsf(operandArray[i].getFConst()) >= 1.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), + diagnostics, &resultArray[i]); + else + foldFloatTypeUnary(operandArray[i], &atanhf, &resultArray[i]); break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return nullptr; - } - break; - case EOpSign: - switch (getType().getBasicType()) - { - case EbtFloat: + case EOpAbs: + switch (getType().getBasicType()) { - float fConst = operandArray[i].getFConst(); - float fResult = 0.0f; - if (fConst > 0.0f) - fResult = 1.0f; - else if (fConst < 0.0f) - fResult = -1.0f; - resultArray[i].setFConst(fResult); + case EbtFloat: + resultArray[i].setFConst(fabsf(operandArray[i].getFConst())); + break; + case EbtInt: + resultArray[i].setIConst(abs(operandArray[i].getIConst())); + break; + default: + UNREACHABLE(); + return nullptr; } break; - case EbtInt: + + case EOpSign: + switch (getType().getBasicType()) { - int iConst = operandArray[i].getIConst(); - int iResult = 0; - if (iConst > 0) - iResult = 1; - else if (iConst < 0) - iResult = -1; - resultArray[i].setIConst(iResult); + case EbtFloat: + { + float fConst = operandArray[i].getFConst(); + float fResult = 0.0f; + if (fConst > 0.0f) + fResult = 1.0f; + else if (fConst < 0.0f) + fResult = -1.0f; + resultArray[i].setFConst(fResult); + break; + } + case EbtInt: + { + int iConst = operandArray[i].getIConst(); + int iResult = 0; + if (iConst > 0) + iResult = 1; + else if (iConst < 0) + iResult = -1; + resultArray[i].setIConst(iResult); + break; + } + default: + UNREACHABLE(); + return nullptr; } break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return nullptr; - } - break; - case EOpFloor: - if (!foldFloatTypeUnary(operandArray[i], &floorf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpFloor: + foldFloatTypeUnary(operandArray[i], &floorf, &resultArray[i]); + break; - case EOpTrunc: - if (!foldFloatTypeUnary(operandArray[i], &truncf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpTrunc: + foldFloatTypeUnary(operandArray[i], &truncf, &resultArray[i]); + break; - case EOpRound: - if (!foldFloatTypeUnary(operandArray[i], &roundf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpRound: + foldFloatTypeUnary(operandArray[i], &roundf, &resultArray[i]); + break; - case EOpRoundEven: - if (getType().getBasicType() == EbtFloat) + case EOpRoundEven: { + ASSERT(getType().getBasicType() == EbtFloat); float x = operandArray[i].getFConst(); float result; float fractPart = modff(x, &result); if (fabsf(fractPart) == 0.5f) result = 2.0f * roundf(x / 2.0f); else result = roundf(x); resultArray[i].setFConst(result); break; } - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return nullptr; - case EOpCeil: - if (!foldFloatTypeUnary(operandArray[i], &ceilf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpCeil: + foldFloatTypeUnary(operandArray[i], &ceilf, &resultArray[i]); + break; - case EOpFract: - if (getType().getBasicType() == EbtFloat) + case EOpFract: { + ASSERT(getType().getBasicType() == EbtFloat); float x = operandArray[i].getFConst(); resultArray[i].setFConst(x - floorf(x)); break; } - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return nullptr; - case EOpIsNan: - if (getType().getBasicType() == EbtFloat) - { + case EOpIsNan: + ASSERT(getType().getBasicType() == EbtFloat); resultArray[i].setBConst(gl::isNaN(operandArray[0].getFConst())); break; - } - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - case EOpIsInf: - if (getType().getBasicType() == EbtFloat) - { + case EOpIsInf: + ASSERT(getType().getBasicType() == EbtFloat); resultArray[i].setBConst(gl::isInf(operandArray[0].getFConst())); break; - } - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - case EOpFloatBitsToInt: - if (getType().getBasicType() == EbtFloat) - { + case EOpFloatBitsToInt: + ASSERT(getType().getBasicType() == EbtFloat); resultArray[i].setIConst(gl::bitCast<int32_t>(operandArray[0].getFConst())); break; - } - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - case EOpFloatBitsToUint: - if (getType().getBasicType() == EbtFloat) - { + case EOpFloatBitsToUint: + ASSERT(getType().getBasicType() == EbtFloat); resultArray[i].setUConst(gl::bitCast<uint32_t>(operandArray[0].getFConst())); break; - } - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - case EOpIntBitsToFloat: - if (getType().getBasicType() == EbtInt) - { + case EOpIntBitsToFloat: + ASSERT(getType().getBasicType() == EbtInt); resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getIConst())); break; - } - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - case EOpUintBitsToFloat: - if (getType().getBasicType() == EbtUInt) - { + case EOpUintBitsToFloat: + ASSERT(getType().getBasicType() == EbtUInt); resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getUConst())); break; - } - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - case EOpExp: - if (!foldFloatTypeUnary(operandArray[i], &expf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpExp: + foldFloatTypeUnary(operandArray[i], &expf, &resultArray[i]); + break; - case EOpLog: - // For log(x), results are undefined if x <= 0, we are choosing to set result to 0. - if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f) - UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); - else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpLog: + // For log(x), results are undefined if x <= 0, we are choosing to set result to 0. + if (operandArray[i].getFConst() <= 0.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), + diagnostics, &resultArray[i]); + else + foldFloatTypeUnary(operandArray[i], &logf, &resultArray[i]); + break; - case EOpExp2: - if (!foldFloatTypeUnary(operandArray[i], &exp2f, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpExp2: + foldFloatTypeUnary(operandArray[i], &exp2f, &resultArray[i]); + break; - case EOpLog2: - // For log2(x), results are undefined if x <= 0, we are choosing to set result to 0. - // And log2f is not available on some plarforms like old android, so just using log(x)/log(2) here. - if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f) - UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); - else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i])) - return nullptr; - else - resultArray[i].setFConst(resultArray[i].getFConst() / logf(2.0f)); - break; + case EOpLog2: + // For log2(x), results are undefined if x <= 0, we are choosing to set result to 0. + // And log2f is not available on some plarforms like old android, so just using + // log(x)/log(2) here. + if (operandArray[i].getFConst() <= 0.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), + diagnostics, &resultArray[i]); + else + { + foldFloatTypeUnary(operandArray[i], &logf, &resultArray[i]); + resultArray[i].setFConst(resultArray[i].getFConst() / logf(2.0f)); + } + break; - case EOpSqrt: - // For sqrt(x), results are undefined if x < 0, we are choosing to set result to 0. - if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 0.0f) - UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); - else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i])) - return nullptr; - break; + case EOpSqrt: + // For sqrt(x), results are undefined if x < 0, we are choosing to set result to 0. + if (operandArray[i].getFConst() < 0.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), + diagnostics, &resultArray[i]); + else + foldFloatTypeUnary(operandArray[i], &sqrtf, &resultArray[i]); + break; - case EOpInverseSqrt: - // There is no stdlib built-in function equavalent for GLES built-in inversesqrt(), - // so getting the square root first using builtin function sqrt() and then taking its inverse. - // Also, for inversesqrt(x), results are undefined if x <= 0, we are choosing to set result to 0. - if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f) - UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); - else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i])) - return nullptr; - else - resultArray[i].setFConst(1.0f / resultArray[i].getFConst()); - break; + case EOpInverseSqrt: + // There is no stdlib built-in function equavalent for GLES built-in inversesqrt(), + // so getting the square root first using builtin function sqrt() and then taking + // its inverse. + // Also, for inversesqrt(x), results are undefined if x <= 0, we are choosing to set + // result to 0. + if (operandArray[i].getFConst() <= 0.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), + diagnostics, &resultArray[i]); + else + { + foldFloatTypeUnary(operandArray[i], &sqrtf, &resultArray[i]); + resultArray[i].setFConst(1.0f / resultArray[i].getFConst()); + } + break; - case EOpVectorLogicalNot: - if (getType().getBasicType() == EbtBool) - { + case EOpVectorLogicalNot: + ASSERT(getType().getBasicType() == EbtBool); resultArray[i].setBConst(!operandArray[i].getBConst()); break; - } - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return nullptr; - case EOpNormalize: - if (getType().getBasicType() == EbtFloat) + case EOpNormalize: { - float x = operandArray[i].getFConst(); + ASSERT(getType().getBasicType() == EbtFloat); + float x = operandArray[i].getFConst(); float length = VectorLength(operandArray, objectSize); if (length) resultArray[i].setFConst(x / length); else - UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, - &resultArray[i]); + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), + diagnostics, &resultArray[i]); break; } - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - case EOpDFdx: - case EOpDFdy: - case EOpFwidth: - if (getType().getBasicType() == EbtFloat) - { + case EOpDFdx: + case EOpDFdy: + case EOpFwidth: + ASSERT(getType().getBasicType() == EbtFloat); // Derivatives of constant arguments should be 0. resultArray[i].setFConst(0.0f); break; - } - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; - default: - return nullptr; + default: + return nullptr; } } return resultArray; } -bool TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion ¶meter, FloatTypeUnaryFunc builtinFunc, - TInfoSink &infoSink, TConstantUnion *result) const +void TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion ¶meter, + FloatTypeUnaryFunc builtinFunc, + TConstantUnion *result) const { ASSERT(builtinFunc); - if (getType().getBasicType() == EbtFloat) - { - result->setFConst(builtinFunc(parameter.getFConst())); - return true; - } - - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return false; + ASSERT(getType().getBasicType() == EbtFloat); + result->setFConst(builtinFunc(parameter.getFConst())); } // static -TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate, - TInfoSink &infoSink) +TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate) { ASSERT(aggregate->getSequence()->size() > 0u); size_t resultSize = aggregate->getType().getObjectSize(); TConstantUnion *resultArray = new TConstantUnion[resultSize]; TBasicType basicType = aggregate->getBasicType(); size_t resultIndex = 0u; @@ -1914,17 +2016,18 @@ TConstantUnion *TIntermConstantUnion::Fo ++resultIndex; } } ASSERT(resultIndex == resultSize); return resultArray; } // static -TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink) +TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate, + TDiagnostics *diagnostics) { TOperator op = aggregate->getOp(); TIntermSequence *sequence = aggregate->getSequence(); unsigned int paramsCount = static_cast<unsigned int>(sequence->size()); std::vector<const TConstantUnion *> unionArrays(paramsCount); std::vector<size_t> objectSizes(paramsCount); size_t maxObjectSize = 0; TBasicType basicType = EbtVoid; @@ -1955,560 +2058,560 @@ TConstantUnion *TIntermConstantUnion::Fo TConstantUnion *resultArray = nullptr; if (paramsCount == 2) { // // Binary built-in // switch (op) { - case EOpAtan: + case EOpAtan: { - if (basicType == EbtFloat) + ASSERT(basicType == EbtFloat); + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) { - resultArray = new TConstantUnion[maxObjectSize]; - for (size_t i = 0; i < maxObjectSize; i++) - { - float y = unionArrays[0][i].getFConst(); - float x = unionArrays[1][i].getFConst(); - // Results are undefined if x and y are both 0. - if (x == 0.0f && y == 0.0f) - UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); - else - resultArray[i].setFConst(atan2f(y, x)); - } + float y = unionArrays[0][i].getFConst(); + float x = unionArrays[1][i].getFConst(); + // Results are undefined if x and y are both 0. + if (x == 0.0f && y == 0.0f) + UndefinedConstantFoldingError(loc, op, basicType, diagnostics, + &resultArray[i]); + else + resultArray[i].setFConst(atan2f(y, x)); } - else - UNREACHABLE(); + break; } - break; - case EOpPow: + case EOpPow: { - if (basicType == EbtFloat) + ASSERT(basicType == EbtFloat); + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) { - resultArray = new TConstantUnion[maxObjectSize]; - for (size_t i = 0; i < maxObjectSize; i++) - { - float x = unionArrays[0][i].getFConst(); - float y = unionArrays[1][i].getFConst(); - // Results are undefined if x < 0. - // Results are undefined if x = 0 and y <= 0. - if (x < 0.0f) - UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); - else if (x == 0.0f && y <= 0.0f) - UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); - else - resultArray[i].setFConst(powf(x, y)); - } + float x = unionArrays[0][i].getFConst(); + float y = unionArrays[1][i].getFConst(); + // Results are undefined if x < 0. + // Results are undefined if x = 0 and y <= 0. + if (x < 0.0f) + UndefinedConstantFoldingError(loc, op, basicType, diagnostics, + &resultArray[i]); + else if (x == 0.0f && y <= 0.0f) + UndefinedConstantFoldingError(loc, op, basicType, diagnostics, + &resultArray[i]); + else + resultArray[i].setFConst(powf(x, y)); } - else - UNREACHABLE(); + break; } - break; - case EOpMod: + case EOpMod: { - if (basicType == EbtFloat) + ASSERT(basicType == EbtFloat); + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) { - resultArray = new TConstantUnion[maxObjectSize]; - for (size_t i = 0; i < maxObjectSize; i++) - { - float x = unionArrays[0][i].getFConst(); - float y = unionArrays[1][i].getFConst(); - resultArray[i].setFConst(x - y * floorf(x / y)); - } + float x = unionArrays[0][i].getFConst(); + float y = unionArrays[1][i].getFConst(); + resultArray[i].setFConst(x - y * floorf(x / y)); } - else - UNREACHABLE(); + break; } - break; - case EOpMin: + case EOpMin: { resultArray = new TConstantUnion[maxObjectSize]; for (size_t i = 0; i < maxObjectSize; i++) { switch (basicType) { - case EbtFloat: - resultArray[i].setFConst(std::min(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst())); - break; - case EbtInt: - resultArray[i].setIConst(std::min(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst())); - break; - case EbtUInt: - resultArray[i].setUConst(std::min(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst())); - break; - default: - UNREACHABLE(); - break; + case EbtFloat: + resultArray[i].setFConst(std::min(unionArrays[0][i].getFConst(), + unionArrays[1][i].getFConst())); + break; + case EbtInt: + resultArray[i].setIConst(std::min(unionArrays[0][i].getIConst(), + unionArrays[1][i].getIConst())); + break; + case EbtUInt: + resultArray[i].setUConst(std::min(unionArrays[0][i].getUConst(), + unionArrays[1][i].getUConst())); + break; + default: + UNREACHABLE(); + break; } } + break; } - break; - case EOpMax: + case EOpMax: { resultArray = new TConstantUnion[maxObjectSize]; for (size_t i = 0; i < maxObjectSize; i++) { switch (basicType) { - case EbtFloat: - resultArray[i].setFConst(std::max(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst())); - break; - case EbtInt: - resultArray[i].setIConst(std::max(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst())); - break; - case EbtUInt: - resultArray[i].setUConst(std::max(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst())); - break; - default: - UNREACHABLE(); - break; + case EbtFloat: + resultArray[i].setFConst(std::max(unionArrays[0][i].getFConst(), + unionArrays[1][i].getFConst())); + break; + case EbtInt: + resultArray[i].setIConst(std::max(unionArrays[0][i].getIConst(), + unionArrays[1][i].getIConst())); + break; + case EbtUInt: + resultArray[i].setUConst(std::max(unionArrays[0][i].getUConst(), + unionArrays[1][i].getUConst())); + break; + default: + UNREACHABLE(); + break; } } + break; } - break; - case EOpStep: + case EOpStep: { - if (basicType == EbtFloat) - { - resultArray = new TConstantUnion[maxObjectSize]; - for (size_t i = 0; i < maxObjectSize; i++) - resultArray[i].setFConst(unionArrays[1][i].getFConst() < unionArrays[0][i].getFConst() ? 0.0f : 1.0f); - } - else - UNREACHABLE(); + ASSERT(basicType == EbtFloat); + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + resultArray[i].setFConst( + unionArrays[1][i].getFConst() < unionArrays[0][i].getFConst() ? 0.0f + : 1.0f); + break; } - break; - case EOpLessThan: + case EOpLessThan: { resultArray = new TConstantUnion[maxObjectSize]; for (size_t i = 0; i < maxObjectSize; i++) { switch (basicType) { - case EbtFloat: - resultArray[i].setBConst(unionArrays[0][i].getFConst() < unionArrays[1][i].getFConst()); - break; - case EbtInt: - resultArray[i].setBConst(unionArrays[0][i].getIConst() < unionArrays[1][i].getIConst()); - break; - case EbtUInt: - resultArray[i].setBConst(unionArrays[0][i].getUConst() < unionArrays[1][i].getUConst()); - break; - default: - UNREACHABLE(); - break; + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() < + unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() < + unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() < + unionArrays[1][i].getUConst()); + break; + default: + UNREACHABLE(); + break; } } + break; } - break; - case EOpLessThanEqual: + case EOpLessThanEqual: { resultArray = new TConstantUnion[maxObjectSize]; for (size_t i = 0; i < maxObjectSize; i++) { switch (basicType) { - case EbtFloat: - resultArray[i].setBConst(unionArrays[0][i].getFConst() <= unionArrays[1][i].getFConst()); - break; - case EbtInt: - resultArray[i].setBConst(unionArrays[0][i].getIConst() <= unionArrays[1][i].getIConst()); - break; - case EbtUInt: - resultArray[i].setBConst(unionArrays[0][i].getUConst() <= unionArrays[1][i].getUConst()); - break; - default: - UNREACHABLE(); - break; + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() <= + unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() <= + unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() <= + unionArrays[1][i].getUConst()); + break; + default: + UNREACHABLE(); + break; } } + break; } - break; - case EOpGreaterThan: + case EOpGreaterThan: { resultArray = new TConstantUnion[maxObjectSize]; for (size_t i = 0; i < maxObjectSize; i++) { switch (basicType) { - case EbtFloat: - resultArray[i].setBConst(unionArrays[0][i].getFConst() > unionArrays[1][i].getFConst()); - break; - case EbtInt: - resultArray[i].setBConst(unionArrays[0][i].getIConst() > unionArrays[1][i].getIConst()); - break; - case EbtUInt: - resultArray[i].setBConst(unionArrays[0][i].getUConst() > unionArrays[1][i].getUConst()); - break; - default: - UNREACHABLE(); - break; + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() > + unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() > + unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() > + unionArrays[1][i].getUConst()); + break; + default: + UNREACHABLE(); + break; } } + break; } - break; - - case EOpGreaterThanEqual: + case EOpGreaterThanEqual: { resultArray = new TConstantUnion[maxObjectSize]; for (size_t i = 0; i < maxObjectSize; i++) { switch (basicType) { - case EbtFloat: - resultArray[i].setBConst(unionArrays[0][i].getFConst() >= unionArrays[1][i].getFConst()); - break; - case EbtInt: - resultArray[i].setBConst(unionArrays[0][i].getIConst() >= unionArrays[1][i].getIConst()); - break; - case EbtUInt: - resultArray[i].setBConst(unionArrays[0][i].getUConst() >= unionArrays[1][i].getUConst()); - break; - default: - UNREACHABLE(); - break; + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() >= + unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() >= + unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() >= + unionArrays[1][i].getUConst()); + break; + default: + UNREACHABLE(); + break; } } } break; - case EOpVectorEqual: + case EOpVectorEqual: { resultArray = new TConstantUnion[maxObjectSize]; for (size_t i = 0; i < maxObjectSize; i++) { switch (basicType) { - case EbtFloat: - resultArray[i].setBConst(unionArrays[0][i].getFConst() == unionArrays[1][i].getFConst()); - break; - case EbtInt: - resultArray[i].setBConst(unionArrays[0][i].getIConst() == unionArrays[1][i].getIConst()); - break; - case EbtUInt: - resultArray[i].setBConst(unionArrays[0][i].getUConst() == unionArrays[1][i].getUConst()); - break; - case EbtBool: - resultArray[i].setBConst(unionArrays[0][i].getBConst() == unionArrays[1][i].getBConst()); - break; - default: - UNREACHABLE(); - break; + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() == + unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() == + unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() == + unionArrays[1][i].getUConst()); + break; + case EbtBool: + resultArray[i].setBConst(unionArrays[0][i].getBConst() == + unionArrays[1][i].getBConst()); + break; + default: + UNREACHABLE(); + break; } } + break; } - break; - case EOpVectorNotEqual: + case EOpVectorNotEqual: { resultArray = new TConstantUnion[maxObjectSize]; for (size_t i = 0; i < maxObjectSize; i++) { switch (basicType) { - case EbtFloat: - resultArray[i].setBConst(unionArrays[0][i].getFConst() != unionArrays[1][i].getFConst()); - break; - case EbtInt: - resultArray[i].setBConst(unionArrays[0][i].getIConst() != unionArrays[1][i].getIConst()); - break; - case EbtUInt: - resultArray[i].setBConst(unionArrays[0][i].getUConst() != unionArrays[1][i].getUConst()); - break; - case EbtBool: - resultArray[i].setBConst(unionArrays[0][i].getBConst() != unionArrays[1][i].getBConst()); - break; - default: - UNREACHABLE(); - break; + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() != + unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() != + unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() != + unionArrays[1][i].getUConst()); + break; + case EbtBool: + resultArray[i].setBConst(unionArrays[0][i].getBConst() != + unionArrays[1][i].getBConst()); + break; + default: + UNREACHABLE(); + break; } } + break; } - break; - case EOpDistance: - if (basicType == EbtFloat) + case EOpDistance: { + ASSERT(basicType == EbtFloat); TConstantUnion *distanceArray = new TConstantUnion[maxObjectSize]; - resultArray = new TConstantUnion(); + resultArray = new TConstantUnion(); for (size_t i = 0; i < maxObjectSize; i++) { float x = unionArrays[0][i].getFConst(); float y = unionArrays[1][i].getFConst(); distanceArray[i].setFConst(x - y); } resultArray->setFConst(VectorLength(distanceArray, maxObjectSize)); - } - else - UNREACHABLE(); - break; - - case EOpDot: - - if (basicType == EbtFloat) - { - resultArray = new TConstantUnion(); - resultArray->setFConst(VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize)); + break; } - else - UNREACHABLE(); - break; - case EOpCross: - if (basicType == EbtFloat && maxObjectSize == 3) + case EOpDot: + ASSERT(basicType == EbtFloat); + resultArray = new TConstantUnion(); + resultArray->setFConst( + VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize)); + break; + + case EOpCross: { + ASSERT(basicType == EbtFloat && maxObjectSize == 3); resultArray = new TConstantUnion[maxObjectSize]; - float x0 = unionArrays[0][0].getFConst(); - float x1 = unionArrays[0][1].getFConst(); - float x2 = unionArrays[0][2].getFConst(); - float y0 = unionArrays[1][0].getFConst(); - float y1 = unionArrays[1][1].getFConst(); - float y2 = unionArrays[1][2].getFConst(); + float x0 = unionArrays[0][0].getFConst(); + float x1 = unionArrays[0][1].getFConst(); + float x2 = unionArrays[0][2].getFConst(); + float y0 = unionArrays[1][0].getFConst(); + float y1 = unionArrays[1][1].getFConst(); + float y2 = unionArrays[1][2].getFConst(); resultArray[0].setFConst(x1 * y2 - y1 * x2); resultArray[1].setFConst(x2 * y0 - y2 * x0); resultArray[2].setFConst(x0 * y1 - y0 * x1); + break; } - else - UNREACHABLE(); - break; - case EOpReflect: - if (basicType == EbtFloat) + case EOpReflect: { + ASSERT(basicType == EbtFloat); // genType reflect (genType I, genType N) : - // For the incident vector I and surface orientation N, returns the reflection direction: + // For the incident vector I and surface orientation N, returns the reflection + // direction: // I - 2 * dot(N, I) * N. - resultArray = new TConstantUnion[maxObjectSize]; + resultArray = new TConstantUnion[maxObjectSize]; float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize); for (size_t i = 0; i < maxObjectSize; i++) { float result = unionArrays[0][i].getFConst() - 2.0f * dotProduct * unionArrays[1][i].getFConst(); resultArray[i].setFConst(result); } + break; } - else - UNREACHABLE(); - break; - case EOpMul: - if (basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() && - (*sequence)[1]->getAsTyped()->isMatrix()) + case EOpMul: { + ASSERT(basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() && + (*sequence)[1]->getAsTyped()->isMatrix()); // Perform component-wise matrix multiplication. resultArray = new TConstantUnion[maxObjectSize]; - int size = (*sequence)[0]->getAsTyped()->getNominalSize(); + int size = (*sequence)[0]->getAsTyped()->getNominalSize(); angle::Matrix<float> result = GetMatrix(unionArrays[0], size).compMult(GetMatrix(unionArrays[1], size)); SetUnionArrayFromMatrix(result, resultArray); + break; } - else - UNREACHABLE(); - break; - case EOpOuterProduct: - if (basicType == EbtFloat) + case EOpOuterProduct: { + ASSERT(basicType == EbtFloat); size_t numRows = (*sequence)[0]->getAsTyped()->getType().getObjectSize(); size_t numCols = (*sequence)[1]->getAsTyped()->getType().getObjectSize(); - resultArray = new TConstantUnion[numRows * numCols]; + resultArray = new TConstantUnion[numRows * numCols]; angle::Matrix<float> result = GetMatrix(unionArrays[0], static_cast<int>(numRows), 1) .outerProduct(GetMatrix(unionArrays[1], 1, static_cast<int>(numCols))); SetUnionArrayFromMatrix(result, resultArray); + break; } - else - UNREACHABLE(); - break; - default: - UNREACHABLE(); - // TODO: Add constant folding support for other built-in operations that take 2 parameters and not handled above. - return nullptr; + default: + UNREACHABLE(); + // TODO: Add constant folding support for other built-in operations that take 2 + // parameters and not handled above. + return nullptr; } } else if (paramsCount == 3) { // // Ternary built-in // switch (op) { - case EOpClamp: + case EOpClamp: { resultArray = new TConstantUnion[maxObjectSize]; for (size_t i = 0; i < maxObjectSize; i++) { switch (basicType) { - case EbtFloat: + case EbtFloat: { - float x = unionArrays[0][i].getFConst(); + float x = unionArrays[0][i].getFConst(); float min = unionArrays[1][i].getFConst(); float max = unionArrays[2][i].getFConst(); // Results are undefined if min > max. if (min > max) - UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + UndefinedConstantFoldingError(loc, op, basicType, diagnostics, + &resultArray[i]); else resultArray[i].setFConst(gl::clamp(x, min, max)); + break; } - break; - case EbtInt: + + case EbtInt: { - int x = unionArrays[0][i].getIConst(); + int x = unionArrays[0][i].getIConst(); int min = unionArrays[1][i].getIConst(); int max = unionArrays[2][i].getIConst(); // Results are undefined if min > max. if (min > max) - UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + UndefinedConstantFoldingError(loc, op, basicType, diagnostics, + &resultArray[i]); else resultArray[i].setIConst(gl::clamp(x, min, max)); + break; } - break; - case EbtUInt: + case EbtUInt: { - unsigned int x = unionArrays[0][i].getUConst(); + unsigned int x = unionArrays[0][i].getUConst(); unsigned int min = unionArrays[1][i].getUConst(); unsigned int max = unionArrays[2][i].getUConst(); // Results are undefined if min > max. if (min > max) - UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + UndefinedConstantFoldingError(loc, op, basicType, diagnostics, + &resultArray[i]); else resultArray[i].setUConst(gl::clamp(x, min, max)); + break; } - break; - default: - UNREACHABLE(); - break; - } - } - } - break; - - case EOpMix: - { - if (basicType == EbtFloat) - { - resultArray = new TConstantUnion[maxObjectSize]; - for (size_t i = 0; i < maxObjectSize; i++) - { - float x = unionArrays[0][i].getFConst(); - float y = unionArrays[1][i].getFConst(); - TBasicType type = (*sequence)[2]->getAsTyped()->getType().getBasicType(); - if (type == EbtFloat) - { - // Returns the linear blend of x and y, i.e., x * (1 - a) + y * a. - float a = unionArrays[2][i].getFConst(); - resultArray[i].setFConst(x * (1.0f - a) + y * a); - } - else // 3rd parameter is EbtBool - { - ASSERT(type == EbtBool); - // Selects which vector each returned component comes from. - // For a component of a that is false, the corresponding component of x is returned. - // For a component of a that is true, the corresponding component of y is returned. - bool a = unionArrays[2][i].getBConst(); - resultArray[i].setFConst(a ? y : x); - } + default: + UNREACHABLE(); + break; } } - else - UNREACHABLE(); + break; } - break; - case EOpSmoothStep: + case EOpMix: { - if (basicType == EbtFloat) + ASSERT(basicType == EbtFloat); + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) { - resultArray = new TConstantUnion[maxObjectSize]; - for (size_t i = 0; i < maxObjectSize; i++) + float x = unionArrays[0][i].getFConst(); + float y = unionArrays[1][i].getFConst(); + TBasicType type = (*sequence)[2]->getAsTyped()->getType().getBasicType(); + if (type == EbtFloat) { - float edge0 = unionArrays[0][i].getFConst(); - float edge1 = unionArrays[1][i].getFConst(); - float x = unionArrays[2][i].getFConst(); - // Results are undefined if edge0 >= edge1. - if (edge0 >= edge1) - { - UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); - } - else - { - // Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth - // Hermite interpolation between 0 and 1 when edge0 < x < edge1. - float t = gl::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); - resultArray[i].setFConst(t * t * (3.0f - 2.0f * t)); - } + // Returns the linear blend of x and y, i.e., x * (1 - a) + y * a. + float a = unionArrays[2][i].getFConst(); + resultArray[i].setFConst(x * (1.0f - a) + y * a); + } + else // 3rd parameter is EbtBool + { + ASSERT(type == EbtBool); + // Selects which vector each returned component comes from. + // For a component of a that is false, the corresponding component of x is + // returned. + // For a component of a that is true, the corresponding component of y is + // returned. + bool a = unionArrays[2][i].getBConst(); + resultArray[i].setFConst(a ? y : x); } } - else - UNREACHABLE(); + break; } - break; - case EOpFaceForward: - if (basicType == EbtFloat) + case EOpSmoothStep: { + ASSERT(basicType == EbtFloat); + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + float edge0 = unionArrays[0][i].getFConst(); + float edge1 = unionArrays[1][i].getFConst(); + float x = unionArrays[2][i].getFConst(); + // Results are undefined if edge0 >= edge1. + if (edge0 >= edge1) + { + UndefinedConstantFoldingError(loc, op, basicType, diagnostics, + &resultArray[i]); + } + else + { + // Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth + // Hermite interpolation between 0 and 1 when edge0 < x < edge1. + float t = gl::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); + resultArray[i].setFConst(t * t * (3.0f - 2.0f * t)); + } + } + break; + } + + case EOpFaceForward: + { + ASSERT(basicType == EbtFloat); // genType faceforward(genType N, genType I, genType Nref) : // If dot(Nref, I) < 0 return N, otherwise return -N. - resultArray = new TConstantUnion[maxObjectSize]; + resultArray = new TConstantUnion[maxObjectSize]; float dotProduct = VectorDotProduct(unionArrays[2], unionArrays[1], maxObjectSize); for (size_t i = 0; i < maxObjectSize; i++) { if (dotProduct < 0) resultArray[i].setFConst(unionArrays[0][i].getFConst()); else resultArray[i].setFConst(-unionArrays[0][i].getFConst()); } + break; } - else - UNREACHABLE(); - break; - case EOpRefract: - if (basicType == EbtFloat) + case EOpRefract: { + ASSERT(basicType == EbtFloat); // genType refract(genType I, genType N, float eta) : - // For the incident vector I and surface normal N, and the ratio of indices of refraction eta, + // For the incident vector I and surface normal N, and the ratio of indices of + // refraction eta, // return the refraction vector. The result is computed by // k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I)) // if (k < 0.0) // return genType(0.0) // else // return eta * I - (eta * dot(N, I) + sqrt(k)) * N - resultArray = new TConstantUnion[maxObjectSize]; + resultArray = new TConstantUnion[maxObjectSize]; float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize); for (size_t i = 0; i < maxObjectSize; i++) { float eta = unionArrays[2][i].getFConst(); - float k = 1.0f - eta * eta * (1.0f - dotProduct * dotProduct); + float k = 1.0f - eta * eta * (1.0f - dotProduct * dotProduct); if (k < 0.0f) resultArray[i].setFConst(0.0f); else resultArray[i].setFConst(eta * unionArrays[0][i].getFConst() - - (eta * dotProduct + sqrtf(k)) * unionArrays[1][i].getFConst()); + (eta * dotProduct + sqrtf(k)) * + unionArrays[1][i].getFConst()); } + break; } - else + + default: UNREACHABLE(); - break; - - default: - UNREACHABLE(); - // TODO: Add constant folding support for other built-in operations that take 3 parameters and not handled above. - return nullptr; + // TODO: Add constant folding support for other built-in operations that take 3 + // parameters and not handled above. + return nullptr; } } return resultArray; } // static TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunction) {
--- a/gfx/angle/src/compiler/translator/IntermNode.h +++ b/gfx/angle/src/compiler/translator/IntermNode.h @@ -29,16 +29,17 @@ class TDiagnostics; class TIntermTraverser; class TIntermAggregate; class TIntermBinary; class TIntermUnary; class TIntermConstantUnion; +class TIntermTernary; class TIntermSelection; class TIntermSwitch; class TIntermCase; class TIntermTyped; class TIntermSymbol; class TIntermLoop; class TInfoSink; class TInfoSinkBase; @@ -88,16 +89,17 @@ class TIntermNode : angle::NonCopyable void setLine(const TSourceLoc &l) { mLine = l; } virtual void traverse(TIntermTraverser *) = 0; virtual TIntermTyped *getAsTyped() { return 0; } virtual TIntermConstantUnion *getAsConstantUnion() { return 0; } virtual TIntermAggregate *getAsAggregate() { return 0; } virtual TIntermBinary *getAsBinaryNode() { return 0; } virtual TIntermUnary *getAsUnaryNode() { return 0; } + virtual TIntermTernary *getAsTernaryNode() { return nullptr; } virtual TIntermSelection *getAsSelectionNode() { return 0; } virtual TIntermSwitch *getAsSwitchNode() { return 0; } virtual TIntermCase *getAsCaseNode() { return 0; } virtual TIntermSymbol *getAsSymbolNode() { return 0; } virtual TIntermLoop *getAsLoopNode() { return 0; } virtual TIntermRaw *getAsRawNode() { return 0; } virtual TIntermBranch *getAsBranchNode() { return 0; } @@ -154,16 +156,19 @@ class TIntermTyped : public TIntermNode bool isScalarInt() const { return mType.isScalarInt(); } const char *getBasicString() const { return mType.getBasicString(); } TString getCompleteString() const { return mType.getCompleteString(); } unsigned int getArraySize() const { return mType.getArraySize(); } bool isConstructorWithOnlyConstantUnionParameters(); + static TIntermTyped *CreateIndexNode(int index); + static TIntermTyped *CreateZero(const TType &type); + protected: TType mType; TIntermTyped(const TIntermTyped &node); }; // // Handle for, do-while, and while loops. @@ -311,16 +316,17 @@ class TIntermRaw : public TIntermTyped // Other nodes than TIntermConstantUnion may also be constant expressions. // class TIntermConstantUnion : public TIntermTyped { public: TIntermConstantUnion(const TConstantUnion *unionPointer, const TType &type) : TIntermTyped(type), mUnionArrayPointer(unionPointer) { + ASSERT(unionPointer); } TIntermTyped *deepCopy() const override { return new TIntermConstantUnion(*this); } bool hasSideEffects() const override { return false; } const TConstantUnion *getUnionArrayPointer() const { return mUnionArrayPointer; } @@ -338,53 +344,56 @@ class TIntermConstantUnion : public TInt } bool getBConst(size_t index) const { return mUnionArrayPointer ? mUnionArrayPointer[index].getBConst() : false; } void replaceConstantUnion(const TConstantUnion *safeConstantUnion) { + ASSERT(safeConstantUnion); // Previous union pointer freed on pool deallocation. mUnionArrayPointer = safeConstantUnion; } TIntermConstantUnion *getAsConstantUnion() override { return this; } void traverse(TIntermTraverser *it) override; bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; } TConstantUnion *foldBinary(TOperator op, TIntermConstantUnion *rightNode, TDiagnostics *diagnostics); - TConstantUnion *foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink); - TConstantUnion *foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink); + const TConstantUnion *foldIndexing(int index); + TConstantUnion *foldUnaryNonComponentWise(TOperator op); + TConstantUnion *foldUnaryComponentWise(TOperator op, TDiagnostics *diagnostics); - static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate, - TInfoSink &infoSink); - static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink); + static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate); + static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate, + TDiagnostics *diagnostics); protected: // Same data may be shared between multiple constant unions, so it can't be modified. const TConstantUnion *mUnionArrayPointer; private: typedef float(*FloatTypeUnaryFunc) (float); - bool foldFloatTypeUnary(const TConstantUnion ¶meter, FloatTypeUnaryFunc builtinFunc, TInfoSink &infoSink, TConstantUnion *result) const; + void foldFloatTypeUnary(const TConstantUnion ¶meter, + FloatTypeUnaryFunc builtinFunc, + TConstantUnion *result) const; TIntermConstantUnion(const TIntermConstantUnion &node); // Note: not deleted, just private! }; // // Intermediate class for node types that hold operators. // class TIntermOperator : public TIntermTyped { public: TOperator getOp() const { return mOp; } - void setOp(TOperator op) { mOp = op; } bool isAssignment() const; bool isMultiplication() const; bool isConstructor() const; bool hasSideEffects() const override { return isAssignment(); } protected: @@ -401,40 +410,33 @@ class TIntermOperator : public TIntermTy }; // // Nodes for all the basic binary math operators. // class TIntermBinary : public TIntermOperator { public: - TIntermBinary(TOperator op) - : TIntermOperator(op), - mAddIndexClamp(false) {} - // This constructor determines the type of the binary node based on the operands and op. - // This is only supported for math/logical ops, not indexing. TIntermBinary(TOperator op, TIntermTyped *left, TIntermTyped *right); TIntermTyped *deepCopy() const override { return new TIntermBinary(*this); } static TOperator GetMulOpBasedOnOperands(const TType &left, const TType &right); static TOperator GetMulAssignOpBasedOnOperands(const TType &left, const TType &right); TIntermBinary *getAsBinaryNode() override { return this; }; void traverse(TIntermTraverser *it) override; bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; bool hasSideEffects() const override { return isAssignment() || mLeft->hasSideEffects() || mRight->hasSideEffects(); } - void setLeft(TIntermTyped *node) { mLeft = node; } - void setRight(TIntermTyped *node) { mRight = node; } TIntermTyped *getLeft() const { return mLeft; } TIntermTyped *getRight() const { return mRight; } TIntermTyped *fold(TDiagnostics *diagnostics); void setAddIndexClamp() { mAddIndexClamp = true; } bool getAddIndexClamp() { return mAddIndexClamp; } protected: @@ -451,49 +453,42 @@ class TIntermBinary : public TIntermOper }; // // Nodes for unary math operators. // class TIntermUnary : public TIntermOperator { public: - TIntermUnary(TOperator op, const TType &type) - : TIntermOperator(op, type), - mOperand(NULL), - mUseEmulatedFunction(false) {} - TIntermUnary(TOperator op) - : TIntermOperator(op), - mOperand(NULL), - mUseEmulatedFunction(false) {} + TIntermUnary(TOperator op, TIntermTyped *operand); TIntermTyped *deepCopy() const override { return new TIntermUnary(*this); } void traverse(TIntermTraverser *it) override; TIntermUnary *getAsUnaryNode() override { return this; } bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; bool hasSideEffects() const override { return isAssignment() || mOperand->hasSideEffects(); } - void setOperand(TIntermTyped *operand) { mOperand = operand; } TIntermTyped *getOperand() { return mOperand; } - void promote(const TType *funcReturnType); - TIntermTyped *fold(TInfoSink &infoSink); + TIntermTyped *fold(TDiagnostics *diagnostics); void setUseEmulatedFunction() { mUseEmulatedFunction = true; } bool getUseEmulatedFunction() { return mUseEmulatedFunction; } protected: TIntermTyped *mOperand; // If set to true, replace the built-in function call with an emulated one // to work around driver bugs. bool mUseEmulatedFunction; private: + void promote(); + TIntermUnary(const TIntermUnary &node); // note: not deleted, just private! }; typedef TVector<TIntermNode *> TIntermSequence; typedef TVector<int> TQualifierList; // // Nodes that operate on an arbitrary sized set of children. @@ -517,26 +512,29 @@ class TIntermAggregate : public TIntermO mGotPrecisionFromChildren(false) { } ~TIntermAggregate() { } // Note: only supported for nodes that can be a part of an expression. TIntermTyped *deepCopy() const override { return new TIntermAggregate(*this); } + void setOp(TOperator op) { mOp = op; } + TIntermAggregate *getAsAggregate() override { return this; } void traverse(TIntermTraverser *it) override; bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; bool replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements); bool insertChildNodes(TIntermSequence::size_type position, TIntermSequence insertions); // Conservatively assume function calls and other aggregate operators have side-effects bool hasSideEffects() const override { return true; } - TIntermTyped *fold(TInfoSink &infoSink); + TIntermTyped *fold(TDiagnostics *diagnostics); TIntermSequence *getSequence() { return &mSequence; } + const TIntermSequence *getSequence() const { return &mSequence; } void setNameObj(const TName &name) { mName = name; } const TName &getNameObj() const { return mName; } void setName(const TString &name) { mName.setString(name); } const TString &getName() const { return mName.getString(); } void setUserDefined() { mUserDefined = true; } @@ -566,56 +564,71 @@ class TIntermAggregate : public TIntermO bool mUseEmulatedFunction; bool mGotPrecisionFromChildren; private: TIntermAggregate(const TIntermAggregate &node); // note: not deleted, just private! }; -// -// For if tests. -// -class TIntermSelection : public TIntermTyped +// For ternary operators like a ? b : c. +class TIntermTernary : public TIntermTyped { public: - TIntermSelection(TIntermTyped *cond, TIntermNode *trueB, TIntermNode *falseB) - : TIntermTyped(TType(EbtVoid, EbpUndefined)), - mCondition(cond), - mTrueBlock(trueB), - mFalseBlock(falseB) {} - TIntermSelection(TIntermTyped *cond, TIntermNode *trueB, TIntermNode *falseB, - const TType &type) - : TIntermTyped(type), - mCondition(cond), - mTrueBlock(trueB), - mFalseBlock(falseB) {} - - // Note: only supported for ternary operator nodes. - TIntermTyped *deepCopy() const override { return new TIntermSelection(*this); } + TIntermTernary(TIntermTyped *cond, TIntermTyped *trueExpression, TIntermTyped *falseExpression); void traverse(TIntermTraverser *it) override; bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; - // Conservatively assume selections have side-effects - bool hasSideEffects() const override { return true; } + TIntermTyped *getCondition() const { return mCondition; } + TIntermTyped *getTrueExpression() const { return mTrueExpression; } + TIntermTyped *getFalseExpression() const { return mFalseExpression; } + TIntermTernary *getAsTernaryNode() override { return this; } + + TIntermTyped *deepCopy() const override { return new TIntermTernary(*this); } + + bool hasSideEffects() const override + { + return mCondition->hasSideEffects() || mTrueExpression->hasSideEffects() || + mFalseExpression->hasSideEffects(); + } + + static TQualifier DetermineQualifier(TIntermTyped *cond, + TIntermTyped *trueExpression, + TIntermTyped *falseExpression); - bool usesTernaryOperator() const { return getBasicType() != EbtVoid; } - TIntermNode *getCondition() const { return mCondition; } + private: + TIntermTernary(const TIntermTernary &node); // Note: not deleted, just private! + + TIntermTyped *mCondition; + TIntermTyped *mTrueExpression; + TIntermTyped *mFalseExpression; +}; + +// For if tests. +class TIntermSelection : public TIntermNode +{ + public: + TIntermSelection(TIntermTyped *cond, TIntermNode *trueB, TIntermNode *falseB) + : TIntermNode(), mCondition(cond), mTrueBlock(trueB), mFalseBlock(falseB) + { + } + + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; + + TIntermTyped *getCondition() const { return mCondition; } TIntermNode *getTrueBlock() const { return mTrueBlock; } TIntermNode *getFalseBlock() const { return mFalseBlock; } TIntermSelection *getAsSelectionNode() override { return this; } protected: TIntermTyped *mCondition; TIntermNode *mTrueBlock; TIntermNode *mFalseBlock; - - private: - TIntermSelection(const TIntermSelection &node); // Note: not deleted, just private! }; // // Switch statement. // class TIntermSwitch : public TIntermNode { public: @@ -691,31 +704,33 @@ class TIntermTraverser : angle::NonCopya TIntermTraverser(bool preVisit, bool inVisit, bool postVisit); virtual ~TIntermTraverser(); virtual void visitSymbol(TIntermSymbol *node) {} virtual void visitRaw(TIntermRaw *node) {} virtual void visitConstantUnion(TIntermConstantUnion *node) {} virtual bool visitBinary(Visit visit, TIntermBinary *node) { return true; } virtual bool visitUnary(Visit visit, TIntermUnary *node) { return true; } + virtual bool visitTernary(Visit visit, TIntermTernary *node) { return true; } virtual bool visitSelection(Visit visit, TIntermSelection *node) { return true; } virtual bool visitSwitch(Visit visit, TIntermSwitch *node) { return true; } virtual bool visitCase(Visit visit, TIntermCase *node) { return true; } virtual bool visitAggregate(Visit visit, TIntermAggregate *node) { return true; } virtual bool visitLoop(Visit visit, TIntermLoop *node) { return true; } virtual bool visitBranch(Visit visit, TIntermBranch *node) { return true; } // The traverse functions contain logic for iterating over the children of the node // and calling the visit functions in the appropriate places. They also track some // context that may be used by the visit functions. virtual void traverseSymbol(TIntermSymbol *node); virtual void traverseRaw(TIntermRaw *node); virtual void traverseConstantUnion(TIntermConstantUnion *node); virtual void traverseBinary(TIntermBinary *node); virtual void traverseUnary(TIntermUnary *node); + virtual void traverseTernary(TIntermTernary *node); virtual void traverseSelection(TIntermSelection *node); virtual void traverseSwitch(TIntermSwitch *node); virtual void traverseCase(TIntermCase *node); virtual void traverseAggregate(TIntermAggregate *node); virtual void traverseLoop(TIntermLoop *node); virtual void traverseBranch(TIntermBranch *node); int getMaxDepth() const { return mMaxDepth; } @@ -993,16 +1008,17 @@ class TMaxDepthTraverser : public TInter public: POOL_ALLOCATOR_NEW_DELETE(); TMaxDepthTraverser(int depthLimit) : TIntermTraverser(true, true, false), mDepthLimit(depthLimit) { } bool visitBinary(Visit, TIntermBinary *) override { return depthCheck(); } bool visitUnary(Visit, TIntermUnary *) override { return depthCheck(); } + bool visitTernary(Visit, TIntermTernary *) override { return depthCheck(); } bool visitSelection(Visit, TIntermSelection *) override { return depthCheck(); } bool visitAggregate(Visit, TIntermAggregate *) override { return depthCheck(); } bool visitLoop(Visit, TIntermLoop *) override { return depthCheck(); } bool visitBranch(Visit, TIntermBranch *) override { return depthCheck(); } protected: bool depthCheck() const { return mMaxDepth < mDepthLimit; }
--- a/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.cpp +++ b/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.cpp @@ -100,19 +100,16 @@ bool IntermNodePatternMatcher::match(TIn { return true; } } } return false; } -bool IntermNodePatternMatcher::match(TIntermSelection *node) +bool IntermNodePatternMatcher::match(TIntermTernary *node) { if ((mMask & kUnfoldedShortCircuitExpression) != 0) { - if (node->usesTernaryOperator()) - { - return true; - } + return true; } return false; }
--- a/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.h +++ b/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.h @@ -9,17 +9,17 @@ // #ifndef COMPILER_TRANSLATOR_INTERMNODEPATTERNMATCHER_H_ #define COMPILER_TRANSLATOR_INTERMNODEPATTERNMATCHER_H_ class TIntermAggregate; class TIntermBinary; class TIntermNode; -class TIntermSelection; +class TIntermTernary; class IntermNodePatternMatcher { public: static bool IsDynamicIndexingOfVectorOrMatrix(TIntermBinary *node); enum PatternType { @@ -37,17 +37,17 @@ class IntermNodePatternMatcher bool match(TIntermBinary *node, TIntermNode *parentNode); // Use this version for checking binary node matches in case you're using flag // kDynamicIndexingOfVectorOrMatrixInLValue. bool match(TIntermBinary *node, TIntermNode *parentNode, bool isLValueRequiredHere); bool match(TIntermAggregate *node, TIntermNode *parentNode); - bool match(TIntermSelection *node); + bool match(TIntermTernary *node); private: const unsigned int mMask; bool matchInternal(TIntermBinary *node, TIntermNode *parentNode); }; #endif
--- a/gfx/angle/src/compiler/translator/IntermTraverse.cpp +++ b/gfx/angle/src/compiler/translator/IntermTraverse.cpp @@ -28,16 +28,21 @@ void TIntermBinary::traverse(TIntermTrav it->traverseBinary(this); } void TIntermUnary::traverse(TIntermTraverser *it) { it->traverseUnary(this); } +void TIntermTernary::traverse(TIntermTraverser *it) +{ + it->traverseTernary(this); +} + void TIntermSelection::traverse(TIntermTraverser *it) { it->traverseSelection(this); } void TIntermSwitch::traverse(TIntermTraverser *it) { it->traverseSwitch(this); @@ -142,37 +147,31 @@ TIntermAggregate *TIntermTraverser::crea return tempDeclaration; } TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier) { ASSERT(initializer != nullptr); TIntermSymbol *tempSymbol = createTempSymbol(initializer->getType(), qualifier); TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration); - TIntermBinary *tempInit = new TIntermBinary(EOpInitialize); - tempInit->setLeft(tempSymbol); - tempInit->setRight(initializer); - tempInit->setType(tempSymbol->getType()); + TIntermBinary *tempInit = new TIntermBinary(EOpInitialize, tempSymbol, initializer); tempDeclaration->getSequence()->push_back(tempInit); return tempDeclaration; } TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer) { return createTempInitDeclaration(initializer, EvqTemporary); } TIntermBinary *TIntermTraverser::createTempAssignment(TIntermTyped *rightNode) { ASSERT(rightNode != nullptr); TIntermSymbol *tempSymbol = createTempSymbol(rightNode->getType()); - TIntermBinary *assignment = new TIntermBinary(EOpAssign); - assignment->setLeft(tempSymbol); - assignment->setRight(rightNode); - assignment->setType(tempSymbol->getType()); + TIntermBinary *assignment = new TIntermBinary(EOpAssign, tempSymbol, rightNode); return assignment; } void TIntermTraverser::useTemporaryIndex(unsigned int *temporaryIndex) { mTemporaryIndex = temporaryIndex; } @@ -568,16 +567,41 @@ void TLValueTrackingTraverser::traverseA decrementDepth(); } if (visit && postVisit) visitAggregate(PostVisit, node); } // +// Traverse a ternary node. Same comments in binary node apply here. +// +void TIntermTraverser::traverseTernary(TIntermTernary *node) +{ + bool visit = true; + + if (preVisit) + visit = visitTernary(PreVisit, node); + + if (visit) + { + incrementDepth(node); + node->getCondition()->traverse(this); + if (node->getTrueExpression()) + node->getTrueExpression()->traverse(this); + if (node->getFalseExpression()) + node->getFalseExpression()->traverse(this); + decrementDepth(); + } + + if (visit && postVisit) + visitTernary(PostVisit, node); +} + +// // Traverse a selection node. Same comments in binary node apply here. // void TIntermTraverser::traverseSelection(TIntermSelection *node) { bool visit = true; if (preVisit) visit = visitSelection(PreVisit, node);
--- a/gfx/angle/src/compiler/translator/Intermediate.cpp +++ b/gfx/angle/src/compiler/translator/Intermediate.cpp @@ -39,48 +39,30 @@ TIntermSymbol *TIntermediate::addSymbol( // // Connect two nodes through an index operator, where the left node is the base // of an array or struct, and the right node is a direct or indirect offset. // // Returns the added node. // The caller should set the type of the returned node. // -TIntermTyped *TIntermediate::addIndex( - TOperator op, TIntermTyped *base, TIntermTyped *index, const TSourceLoc &line) +TIntermTyped *TIntermediate::addIndex(TOperator op, + TIntermTyped *base, + TIntermTyped *index, + const TSourceLoc &line, + TDiagnostics *diagnostics) { - TIntermBinary *node = new TIntermBinary(op); + TIntermBinary *node = new TIntermBinary(op, base, index); node->setLine(line); - node->setLeft(base); - node->setRight(index); - - // caller should set the type - - return node; -} -// -// Add one node as the parent of another that it operates on. -// -// Returns the added node. -// -TIntermTyped *TIntermediate::addUnaryMath( - TOperator op, TIntermTyped *child, const TSourceLoc &line, const TType *funcReturnType) -{ - // - // Make a new node for the operator. - // - TIntermUnary *node = new TIntermUnary(op); - node->setLine(line); - node->setOperand(child); - node->promote(funcReturnType); - - TIntermTyped *foldedNode = node->fold(mInfoSink); - if (foldedNode) - return foldedNode; + TIntermTyped *folded = node->fold(diagnostics); + if (folded) + { + return folded; + } return node; } // // This is the safe way to change the operator on an aggregate, as it // does lots of error checking and fixing. Especially for establishing // a function call's operation on it's set of parameters. Sequences @@ -249,53 +231,47 @@ TIntermTyped *TIntermediate::addComma(TI commaNode = growAggregate(left, right, line); commaNode->getAsAggregate()->setOp(EOpComma); commaNode->setType(right->getType()); } commaNode->getTypePointer()->setQualifier(resultQualifier); return commaNode; } -// // For "?:" test nodes. There are three children; a condition, // a true path, and a false path. The two paths are specified // as separate parameters. // -// Returns the selection node created, or one of trueBlock and falseBlock if the expression could be folded. -// -TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, - const TSourceLoc &line) +// Returns the ternary node created, or one of trueExpression and falseExpression if the expression +// could be folded. +TIntermTyped *TIntermediate::AddTernarySelection(TIntermTyped *cond, + TIntermTyped *trueExpression, + TIntermTyped *falseExpression, + const TSourceLoc &line) { - TQualifier resultQualifier = EvqTemporary; - if (cond->getQualifier() == EvqConst && trueBlock->getQualifier() == EvqConst && - falseBlock->getQualifier() == EvqConst) - { - resultQualifier = EvqConst; - } // Note that the node resulting from here can be a constant union without being qualified as // constant. if (cond->getAsConstantUnion()) { + TQualifier resultQualifier = + TIntermTernary::DetermineQualifier(cond, trueExpression, falseExpression); if (cond->getAsConstantUnion()->getBConst(0)) { - trueBlock->getTypePointer()->setQualifier(resultQualifier); - return trueBlock; + trueExpression->getTypePointer()->setQualifier(resultQualifier); + return trueExpression; } else { - falseBlock->getTypePointer()->setQualifier(resultQualifier); - return falseBlock; + falseExpression->getTypePointer()->setQualifier(resultQualifier); + return falseExpression; } } - // - // Make a selection node. - // - TIntermSelection *node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType()); - node->getTypePointer()->setQualifier(resultQualifier); + // Make a ternary node. + TIntermTernary *node = new TIntermTernary(cond, trueExpression, falseExpression); node->setLine(line); return node; } TIntermSwitch *TIntermediate::addSwitch( TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &line) { @@ -330,16 +306,17 @@ TIntermConstantUnion *TIntermediate::add return node; } TIntermTyped *TIntermediate::addSwizzle( TVectorFields &fields, const TSourceLoc &line) { TIntermAggregate *node = new TIntermAggregate(EOpSequence); + node->getTypePointer()->setQualifier(EvqConst); node->setLine(line); TIntermConstantUnion *constIntNode; TIntermSequence *sequenceVector = node->getSequence(); TConstantUnion *unionArray; for (int i = 0; i < fields.num; i++) { @@ -383,17 +360,17 @@ TIntermBranch* TIntermediate::addBranch( return node; } // // This is to be executed once the final root is put on top by the parsing // process. // -TIntermAggregate *TIntermediate::postProcess(TIntermNode *root) +TIntermAggregate *TIntermediate::PostProcess(TIntermNode *root) { if (root == nullptr) return nullptr; // // Finish off the top level sequence, if any // TIntermAggregate *aggRoot = root->getAsAggregate(); @@ -406,17 +383,18 @@ TIntermAggregate *TIntermediate::postPro aggRoot = new TIntermAggregate(EOpSequence); aggRoot->setLine(root->getLine()); aggRoot->getSequence()->push_back(root); } return aggRoot; } -TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate) +TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate, + TDiagnostics *diagnostics) { switch (aggregate->getOp()) { case EOpAtan: case EOpPow: case EOpMod: case EOpMin: case EOpMax: @@ -433,19 +411,19 @@ TIntermTyped *TIntermediate::foldAggrega case EOpVectorEqual: case EOpVectorNotEqual: case EOpDistance: case EOpDot: case EOpCross: case EOpFaceForward: case EOpReflect: case EOpRefract: - return aggregate->fold(mInfoSink); + return aggregate->fold(diagnostics); default: // TODO: Add support for folding array constructors if (aggregate->isConstructor() && !aggregate->isArray()) { - return aggregate->fold(mInfoSink); + return aggregate->fold(diagnostics); } // Constant folding not supported for the built-in. return nullptr; } }
--- a/gfx/angle/src/compiler/translator/Intermediate.h +++ b/gfx/angle/src/compiler/translator/Intermediate.h @@ -11,61 +11,62 @@ struct TVectorFields { int offsets[4]; int num; }; // -// Set of helper functions to help parse and build the tree. +// Set of helper functions to help build the tree. // -class TInfoSink; class TIntermediate { public: POOL_ALLOCATOR_NEW_DELETE(); - TIntermediate(TInfoSink &i) - : mInfoSink(i) { } + TIntermediate() {} TIntermSymbol *addSymbol( int id, const TString &, const TType &, const TSourceLoc &); - TIntermTyped *addIndex( - TOperator op, TIntermTyped *base, TIntermTyped *index, const TSourceLoc &); + TIntermTyped *addIndex(TOperator op, + TIntermTyped *base, + TIntermTyped *index, + const TSourceLoc &line, + TDiagnostics *diagnostics); TIntermTyped *addUnaryMath( TOperator op, TIntermTyped *child, const TSourceLoc &line, const TType *funcReturnType); TIntermAggregate *growAggregate( TIntermNode *left, TIntermNode *right, const TSourceLoc &); TIntermAggregate *makeAggregate(TIntermNode *node, const TSourceLoc &); TIntermAggregate *ensureSequence(TIntermNode *node); TIntermAggregate *setAggregateOperator(TIntermNode *, TOperator, const TSourceLoc &); - TIntermNode *addSelection(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &); - TIntermTyped *addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, - const TSourceLoc &line); + TIntermNode *addSelection(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &line); + static TIntermTyped *AddTernarySelection(TIntermTyped *cond, + TIntermTyped *trueExpression, + TIntermTyped *falseExpression, + const TSourceLoc &line); TIntermSwitch *addSwitch( TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &line); TIntermCase *addCase( TIntermTyped *condition, const TSourceLoc &line); TIntermTyped *addComma(TIntermTyped *left, TIntermTyped *right, const TSourceLoc &line, int shaderVersion); TIntermConstantUnion *addConstantUnion(const TConstantUnion *constantUnion, const TType &type, const TSourceLoc &line); TIntermNode *addLoop(TLoopType, TIntermNode *, TIntermTyped *, TIntermTyped *, TIntermNode *, const TSourceLoc &); TIntermBranch *addBranch(TOperator, const TSourceLoc &); TIntermBranch *addBranch(TOperator, TIntermTyped *, const TSourceLoc &); TIntermTyped *addSwizzle(TVectorFields &, const TSourceLoc &); - TIntermAggregate *postProcess(TIntermNode *root); + static TIntermAggregate *PostProcess(TIntermNode *root); static void outputTree(TIntermNode *, TInfoSinkBase &); - TIntermTyped *foldAggregateBuiltIn(TIntermAggregate *aggregate); + TIntermTyped *foldAggregateBuiltIn(TIntermAggregate *aggregate, TDiagnostics *diagnostics); private: void operator=(TIntermediate &); // prevent assignments - - TInfoSink & mInfoSink; }; #endif // COMPILER_TRANSLATOR_INTERMEDIATE_H_
--- a/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp +++ b/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp @@ -22,21 +22,19 @@ TString arrayBrackets(const TType &type) bool isSingleStatement(TIntermNode *node) { if (const TIntermAggregate *aggregate = node->getAsAggregate()) { return (aggregate->getOp() != EOpFunction) && (aggregate->getOp() != EOpSequence); } - else if (const TIntermSelection *selection = node->getAsSelectionNode()) + else if (node->getAsSelectionNode()) { - // Ternary operators are usually part of an assignment operator. - // This handles those rare cases in which they are all by themselves. - return selection->usesTernaryOperator(); + return false; } else if (node->getAsLoopNode()) { return false; } else if (node->getAsSwitchNode()) { return false; @@ -706,50 +704,50 @@ bool TOutputGLSLBase::visitUnary(Visit v if (visit == PreVisit && node->getUseEmulatedFunction()) preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preString); writeTriplet(visit, preString.c_str(), NULL, postString.c_str()); return true; } +bool TOutputGLSLBase::visitTernary(Visit visit, TIntermTernary *node) +{ + TInfoSinkBase &out = objSink(); + // Notice two brackets at the beginning and end. The outer ones + // encapsulate the whole ternary expression. This preserves the + // order of precedence when ternary expressions are used in a + // compound expression, i.e., c = 2 * (a < b ? 1 : 2). + out << "(("; + node->getCondition()->traverse(this); + out << ") ? ("; + node->getTrueExpression()->traverse(this); + out << ") : ("; + node->getFalseExpression()->traverse(this); + out << "))"; + return false; +} + bool TOutputGLSLBase::visitSelection(Visit visit, TIntermSelection *node) { TInfoSinkBase &out = objSink(); - if (node->usesTernaryOperator()) + out << "if ("; + node->getCondition()->traverse(this); + out << ")\n"; + + incrementDepth(node); + visitCodeBlock(node->getTrueBlock()); + + if (node->getFalseBlock()) { - // Notice two brackets at the beginning and end. The outer ones - // encapsulate the whole ternary expression. This preserves the - // order of precedence when ternary expressions are used in a - // compound expression, i.e., c = 2 * (a < b ? 1 : 2). - out << "(("; - node->getCondition()->traverse(this); - out << ") ? ("; - node->getTrueBlock()->traverse(this); - out << ") : ("; - node->getFalseBlock()->traverse(this); - out << "))"; + out << "else\n"; + visitCodeBlock(node->getFalseBlock()); } - else - { - out << "if ("; - node->getCondition()->traverse(this); - out << ")\n"; - - incrementDepth(node); - visitCodeBlock(node->getTrueBlock()); - - if (node->getFalseBlock()) - { - out << "else\n"; - visitCodeBlock(node->getFalseBlock()); - } - decrementDepth(); - } + decrementDepth(); return false; } bool TOutputGLSLBase::visitSwitch(Visit visit, TIntermSwitch *node) { if (node->getStatementList()) { writeTriplet(visit, "switch (", ") ", nullptr);
--- a/gfx/angle/src/compiler/translator/OutputGLSLBase.h +++ b/gfx/angle/src/compiler/translator/OutputGLSLBase.h @@ -39,16 +39,17 @@ class TOutputGLSLBase : public TIntermTr const TConstantUnion *writeConstantUnion(const TType &type, const TConstantUnion *pConstUnion); void writeConstructorTriplet(Visit visit, const TType &type); TString getTypeName(const TType &type); void visitSymbol(TIntermSymbol *node) override; void visitConstantUnion(TIntermConstantUnion *node) override; bool visitBinary(Visit visit, TIntermBinary *node) override; bool visitUnary(Visit visit, TIntermUnary *node) override; + bool visitTernary(Visit visit, TIntermTernary *node) override; bool visitSelection(Visit visit, TIntermSelection *node) override; bool visitSwitch(Visit visit, TIntermSwitch *node) override; bool visitCase(Visit visit, TIntermCase *node) override; bool visitAggregate(Visit visit, TIntermAggregate *node) override; bool visitLoop(Visit visit, TIntermLoop *node) override; bool visitBranch(Visit visit, TIntermBranch *node) override; void visitCodeBlock(TIntermNode *node);
--- a/gfx/angle/src/compiler/translator/OutputHLSL.cpp +++ b/gfx/angle/src/compiler/translator/OutputHLSL.cpp @@ -75,21 +75,24 @@ const TConstantUnion *WriteConstantUnion return constUnionIterated; } } // namespace namespace sh { -OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, - const TExtensionBehavior &extensionBehavior, - const char *sourcePath, ShShaderOutput outputType, - int numRenderTargets, const std::vector<Uniform> &uniforms, - int compileOptions) +OutputHLSL::OutputHLSL(sh::GLenum shaderType, + int shaderVersion, + const TExtensionBehavior &extensionBehavior, + const char *sourcePath, + ShShaderOutput outputType, + int numRenderTargets, + const std::vector<Uniform> &uniforms, + ShCompileOptions compileOptions) : TIntermTraverser(true, true, true), mShaderType(shaderType), mShaderVersion(shaderVersion), mExtensionBehavior(extensionBehavior), mSourcePath(sourcePath), mOutputType(outputType), mCompileOptions(compileOptions), mNumRenderTargets(numRenderTargets), @@ -1451,19 +1454,18 @@ bool OutputHLSL::visitAggregate(Visit vi (*sit)->traverse(this); // Don't output ; after case labels, they're terminated by : // This is needed especially since outputting a ; after a case statement would turn empty // case statements into non-empty case statements, disallowing fall-through from them. // Also no need to output ; after selection (if) statements or sequences. This is done just // for code clarity. - TIntermSelection *asSelection = (*sit)->getAsSelectionNode(); - ASSERT(asSelection == nullptr || !asSelection->usesTernaryOperator()); - if ((*sit)->getAsCaseNode() == nullptr && asSelection == nullptr && !IsSequence(*sit)) + if ((*sit)->getAsCaseNode() == nullptr && (*sit)->getAsSelectionNode() == nullptr && + !IsSequence(*sit)) out << ";\n"; } if (mInsideFunction) { outputLineDirective(out, node->getLine().last_line); out << "}\n"; } @@ -1977,21 +1979,28 @@ void OutputHLSL::writeSelection(TInfoSin // ANGLE issue 486: Detect problematic conditional discard if (discard) { mUsesDiscardRewriting = true; } } +bool OutputHLSL::visitTernary(Visit, TIntermTernary *) +{ + // Ternary ops should have been already converted to something else in the AST. HLSL ternary + // operator doesn't short-circuit, so it's not the same as the GLSL ternary operator. + UNREACHABLE(); + return false; +} + bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node) { TInfoSinkBase &out = getInfoSink(); - ASSERT(!node->usesTernaryOperator()); ASSERT(mInsideFunction); // D3D errors when there is a gradient operation in a loop in an unflattened if. if (mShaderType == GL_FRAGMENT_SHADER && mCurrentFunctionMetadata->hasGradientLoop(node)) { out << "FLATTEN "; }
--- a/gfx/angle/src/compiler/translator/OutputHLSL.h +++ b/gfx/angle/src/compiler/translator/OutputHLSL.h @@ -25,21 +25,24 @@ class TextureFunctionHLSL; class UnfoldShortCircuit; class UniformHLSL; typedef std::map<TString, TIntermSymbol*> ReferencedSymbols; class OutputHLSL : public TIntermTraverser { public: - OutputHLSL(sh::GLenum shaderType, int shaderVersion, - const TExtensionBehavior &extensionBehavior, - const char *sourcePath, ShShaderOutput outputType, - int numRenderTargets, const std::vector<Uniform> &uniforms, - int compileOptions); + OutputHLSL(sh::GLenum shaderType, + int shaderVersion, + const TExtensionBehavior &extensionBehavior, + const char *sourcePath, + ShShaderOutput outputType, + int numRenderTargets, + const std::vector<Uniform> &uniforms, + ShCompileOptions compileOptions); ~OutputHLSL(); void output(TIntermNode *treeRoot, TInfoSinkBase &objSink); const std::map<std::string, unsigned int> &getInterfaceBlockRegisterMap() const; const std::map<std::string, unsigned int> &getUniformRegisterMap() const; @@ -53,16 +56,17 @@ class OutputHLSL : public TIntermTravers void header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator); // Visit AST nodes and output their code to the body stream void visitSymbol(TIntermSymbol*); void visitRaw(TIntermRaw*); void visitConstantUnion(TIntermConstantUnion*); bool visitBinary(Visit visit, TIntermBinary*); bool visitUnary(Visit visit, TIntermUnary*); + bool visitTernary(Visit visit, TIntermTernary *); bool visitSelection(Visit visit, TIntermSelection*); bool visitSwitch(Visit visit, TIntermSwitch *); bool visitCase(Visit visit, TIntermCase *); bool visitAggregate(Visit visit, TIntermAggregate*); bool visitLoop(Visit visit, TIntermLoop*); bool visitBranch(Visit visit, TIntermBranch*); bool isSingleStatement(TIntermNode *node); @@ -112,17 +116,17 @@ class OutputHLSL : public TIntermTravers // Ensures if the type is a struct, the struct is defined void ensureStructDefined(const TType &type); sh::GLenum mShaderType; int mShaderVersion; const TExtensionBehavior &mExtensionBehavior; const char *mSourcePath; const ShShaderOutput mOutputType; - int mCompileOptions; + ShCompileOptions mCompileOptions; bool mInsideFunction; // Output streams TInfoSinkBase mHeader; TInfoSinkBase mBody; TInfoSinkBase mFooter;
--- a/gfx/angle/src/compiler/translator/ParseContext.cpp +++ b/gfx/angle/src/compiler/translator/ParseContext.cpp @@ -208,16 +208,22 @@ void TParseContext::binaryOpError(const } void TParseContext::checkPrecisionSpecified(const TSourceLoc &line, TPrecision precision, TBasicType type) { if (!mChecksPrecisionErrors) return; + + if (precision != EbpUndefined && !SupportsPrecision(type)) + { + error(line, "illegal type for precision qualifier", getBasicString(type)); + } + if (precision == EbpUndefined) { switch (type) { case EbtFloat: error(line, "No precision specified for (float)", ""); return; case EbtInt: @@ -442,21 +448,16 @@ bool TParseContext::checkIsNotReserved(c error(line, reservedErrMsg, "webgl_"); return false; } if (identifier.compare(0, 7, "_webgl_") == 0) { error(line, reservedErrMsg, "_webgl_"); return false; } - if (mShaderSpec == SH_CSS_SHADERS_SPEC && identifier.compare(0, 4, "css_") == 0) - { - error(line, reservedErrMsg, "css_"); - return false; - } } if (identifier.find("__") != TString::npos) { error(line, "identifiers containing two consecutive underscores (__) are reserved as " "possible future keywords", identifier.c_str()); return false; @@ -656,24 +657,24 @@ void TParseContext::checkIsScalarBool(co error(line, "boolean expression expected", ""); } } // This function checks to see if the node (for the expression) contains a scalar boolean expression // or not. void TParseContext::checkIsScalarBool(const TSourceLoc &line, const TPublicType &pType) { - if (pType.type != EbtBool || pType.isAggregate()) + if (pType.getBasicType() != EbtBool || pType.isAggregate()) { error(line, "boolean expression expected", ""); } } bool TParseContext::checkIsNotSampler(const TSourceLoc &line, - const TPublicType &pType, + const TTypeSpecifierNonArray &pType, const char *reason) { if (pType.type == EbtStruct) { if (containsSampler(*pType.userDef)) { error(line, reason, getBasicString(pType.type), "(structure contains a sampler)"); return false; @@ -818,17 +819,17 @@ bool TParseContext::checkIsValidTypeForA { error(line, "cannot declare arrays of arrays", TType(elementType).getCompleteString().c_str()); return false; } // In ESSL1.00 shaders, structs cannot be varying (section 4.3.5). This is checked elsewhere. // In ESSL3.00 shaders, struct inputs/outputs are allowed but not arrays of structs (section // 4.3.4). - if (mShaderVersion >= 300 && elementType.type == EbtStruct && + if (mShaderVersion >= 300 && elementType.getBasicType() == EbtStruct && sh::IsVarying(elementType.qualifier)) { error(line, "cannot declare arrays of structs of this qualifier", TType(elementType).getCompleteString().c_str()); return false; } return true; @@ -923,37 +924,34 @@ bool TParseContext::declareVariable(cons } if (!checkIsNonVoid(line, identifier, type.getBasicType())) return false; return true; } -void TParseContext::checkIsParameterQualifierValid(const TSourceLoc &line, - TQualifier qualifier, - TQualifier paramQualifier, - TType *type) +void TParseContext::checkIsParameterQualifierValid( + const TSourceLoc &line, + const TTypeQualifierBuilder &typeQualifierBuilder, + TType *type) { - if (qualifier != EvqConst && qualifier != EvqTemporary) + TTypeQualifier typeQualifier = typeQualifierBuilder.getParameterTypeQualifier(&mDiagnostics); + + if (typeQualifier.qualifier == EvqOut || typeQualifier.qualifier == EvqInOut) { - error(line, "qualifier not allowed on function parameter", getQualifierString(qualifier)); - return; + checkOutParameterIsNotSampler(line, typeQualifier.qualifier, *type); } - if (qualifier == EvqConst && paramQualifier != EvqIn) + + type->setQualifier(typeQualifier.qualifier); + + if (typeQualifier.precision != EbpUndefined) { - error(line, "qualifier not allowed with ", getQualifierString(qualifier), - getQualifierString(paramQualifier)); - return; + type->setPrecision(typeQualifier.precision); } - - if (qualifier == EvqConst) - type->setQualifier(EvqConstReadOnly); - else - type->setQualifier(paramQualifier); } bool TParseContext::checkCanUseExtension(const TSourceLoc &line, const TString &extension) { const TExtensionBehavior &extBehavior = extensionBehavior(); TExtensionBehavior::const_iterator iter = extBehavior.find(extension.c_str()); if (iter == extBehavior.end()) { @@ -983,29 +981,30 @@ void TParseContext::singleDeclarationErr switch (publicType.qualifier) { case EvqVaryingIn: case EvqVaryingOut: case EvqAttribute: case EvqVertexIn: case EvqFragmentOut: case EvqComputeIn: - if (publicType.type == EbtStruct) + if (publicType.getBasicType() == EbtStruct) { error(identifierLocation, "cannot be used with a structure", getQualifierString(publicType.qualifier)); return; } default: break; } if (publicType.qualifier != EvqUniform && - !checkIsNotSampler(identifierLocation, publicType, "samplers must be uniform")) + !checkIsNotSampler(identifierLocation, publicType.typeSpecifierNonArray, + "samplers must be uniform")) { return; } // check for layout qualifier issues const TLayoutQualifier layoutQualifier = publicType.layoutQualifier; if (layoutQualifier.matrixPacking != EmpUnspecified) @@ -1072,22 +1071,37 @@ void TParseContext::functionCallLValueEr error(argument->getLine(), "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error"); return; } } } } -void TParseContext::checkInvariantIsOutVariableES3(const TQualifier qualifier, - const TSourceLoc &invariantLocation) +void TParseContext::checkInvariantVariableQualifier(bool invariant, + const TQualifier qualifier, + const TSourceLoc &invariantLocation) { - if (!sh::IsVaryingOut(qualifier) && qualifier != EvqFragmentOut) + if (!invariant) + return; + + if (mShaderVersion < 300) { - error(invariantLocation, "Only out variables can be invariant.", "invariant"); + // input variables in the fragment shader can be also qualified as invariant + if (!sh::CanBeInvariantESSL1(qualifier)) + { + error(invariantLocation, "Cannot be qualified as invariant.", "invariant"); + } + } + else + { + if (!sh::CanBeInvariantESSL3OrGreater(qualifier)) + { + error(invariantLocation, "Cannot be qualified as invariant.", "invariant"); + } } } bool TParseContext::supportsExtension(const char *extension) { const TExtensionBehavior &extbehavior = extensionBehavior(); TExtensionBehavior::const_iterator iter = extbehavior.find(extension); return (iter != extbehavior.end()); @@ -1380,74 +1394,90 @@ bool TParseContext::executeInitializer(c { assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); return true; } return false; } -TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier, - bool invariant, - TLayoutQualifier layoutQualifier, +TPublicType TParseContext::addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder, const TPublicType &typeSpecifier) { + TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics); + TPublicType returnType = typeSpecifier; - returnType.qualifier = qualifier; - returnType.invariant = invariant; - returnType.layoutQualifier = layoutQualifier; - - checkWorkGroupSizeIsNotSpecified(typeSpecifier.line, layoutQualifier); + returnType.qualifier = typeQualifier.qualifier; + returnType.invariant = typeQualifier.invariant; + returnType.layoutQualifier = typeQualifier.layoutQualifier; + returnType.precision = typeSpecifier.precision; + + if (typeQualifier.precision != EbpUndefined) + { + returnType.precision = typeQualifier.precision; + } + + checkPrecisionSpecified(typeSpecifier.getLine(), returnType.precision, + typeSpecifier.getBasicType()); + + checkInvariantVariableQualifier(returnType.invariant, returnType.qualifier, + typeSpecifier.getLine()); + + checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), returnType.layoutQualifier); if (mShaderVersion < 300) { if (typeSpecifier.array) { - error(typeSpecifier.line, "not supported", "first-class array"); + error(typeSpecifier.getLine(), "not supported", "first-class array"); returnType.clearArrayness(); } - if (qualifier == EvqAttribute && - (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt)) + if (returnType.qualifier == EvqAttribute && + (typeSpecifier.getBasicType() == EbtBool || typeSpecifier.getBasicType() == EbtInt)) { - error(typeSpecifier.line, "cannot be bool or int", getQualifierString(qualifier)); + error(typeSpecifier.getLine(), "cannot be bool or int", + getQualifierString(returnType.qualifier)); } - if ((qualifier == EvqVaryingIn || qualifier == EvqVaryingOut) && - (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt)) + if ((returnType.qualifier == EvqVaryingIn || returnType.qualifier == EvqVaryingOut) && + (typeSpecifier.getBasicType() == EbtBool || typeSpecifier.getBasicType() == EbtInt)) { - error(typeSpecifier.line, "cannot be bool or int", getQualifierString(qualifier)); + error(typeSpecifier.getLine(), "cannot be bool or int", + getQualifierString(returnType.qualifier)); } } else { - if (!layoutQualifier.isEmpty()) + if (!returnType.layoutQualifier.isEmpty()) { - checkIsAtGlobalLevel(typeSpecifier.line, "layout"); + checkIsAtGlobalLevel(typeSpecifier.getLine(), "layout"); } - if (sh::IsVarying(qualifier) || qualifier == EvqVertexIn || qualifier == EvqFragmentOut) + if (sh::IsVarying(returnType.qualifier) || returnType.qualifier == EvqVertexIn || + returnType.qualifier == EvqFragmentOut) { - checkInputOutputTypeIsValidES3(qualifier, typeSpecifier, typeSpecifier.line); + checkInputOutputTypeIsValidES3(returnType.qualifier, typeSpecifier, + typeSpecifier.getLine()); } - if (qualifier == EvqComputeIn) + if (returnType.qualifier == EvqComputeIn) { - error(typeSpecifier.line, "'in' can be only used to specify the local group size", + error(typeSpecifier.getLine(), "'in' can be only used to specify the local group size", "in"); } } return returnType; } void TParseContext::checkInputOutputTypeIsValidES3(const TQualifier qualifier, const TPublicType &type, const TSourceLoc &qualifierLocation) { // An input/output variable can never be bool or a sampler. Samplers are checked elsewhere. - if (type.type == EbtBool) + if (type.getBasicType() == EbtBool) { error(qualifierLocation, "cannot be bool", getQualifierString(qualifier)); } // Specific restrictions apply for vertex shader inputs and fragment shader outputs. switch (qualifier) { case EvqVertexIn: @@ -1455,38 +1485,38 @@ void TParseContext::checkInputOutputType if (type.array) { error(qualifierLocation, "cannot be array", getQualifierString(qualifier)); } // Vertex inputs with a struct type are disallowed in singleDeclarationErrorCheck return; case EvqFragmentOut: // ESSL 3.00 section 4.3.6 - if (type.isMatrix()) + if (type.typeSpecifierNonArray.isMatrix()) { error(qualifierLocation, "cannot be matrix", getQualifierString(qualifier)); } // Fragment outputs with a struct type are disallowed in singleDeclarationErrorCheck return; default: break; } // Vertex shader outputs / fragment shader inputs have a different, slightly more lenient set of // restrictions. bool typeContainsIntegers = - (type.type == EbtInt || type.type == EbtUInt || type.isStructureContainingType(EbtInt) || - type.isStructureContainingType(EbtUInt)); + (type.getBasicType() == EbtInt || type.getBasicType() == EbtUInt || + type.isStructureContainingType(EbtInt) || type.isStructureContainingType(EbtUInt)); if (typeContainsIntegers && qualifier != EvqFlatIn && qualifier != EvqFlatOut) { error(qualifierLocation, "must use 'flat' interpolation here", getQualifierString(qualifier)); } - if (type.type == EbtStruct) + if (type.getBasicType() == EbtStruct) { // ESSL 3.00 sections 4.3.4 and 4.3.6. // These restrictions are only implied by the ESSL 3.00 spec, but // the ESSL 3.10 spec lists these restrictions explicitly. if (type.array) { error(qualifierLocation, "cannot be an array of structures", getQualifierString(qualifier)); @@ -1663,50 +1693,68 @@ TIntermAggregate *TParseContext::parseSi return initNode ? intermediate.makeAggregate(initNode, initLocation) : nullptr; } else { return nullptr; } } -TIntermAggregate *TParseContext::parseInvariantDeclaration(const TSourceLoc &invariantLoc, - const TSourceLoc &identifierLoc, - const TString *identifier, - const TSymbol *symbol) +TIntermAggregate *TParseContext::parseInvariantDeclaration( + const TTypeQualifierBuilder &typeQualifierBuilder, + const TSourceLoc &identifierLoc, + const TString *identifier, + const TSymbol *symbol) { - // invariant declaration - if (!checkIsAtGlobalLevel(invariantLoc, "invariant varying")) + TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics); + + if (!typeQualifier.invariant) + { + error(identifierLoc, "Expected invariant", identifier->c_str()); return nullptr; - + } + if (!checkIsAtGlobalLevel(identifierLoc, "invariant varying")) + { + return nullptr; + } if (!symbol) { error(identifierLoc, "undeclared identifier declared as invariant", identifier->c_str()); return nullptr; } - else + if (!IsQualifierUnspecified(typeQualifier.qualifier)) + { + error(identifierLoc, "invariant declaration specifies qualifier", + getQualifierString(typeQualifier.qualifier)); + } + if (typeQualifier.precision != EbpUndefined) + { + error(identifierLoc, "invariant declaration specifies precision", + getPrecisionString(typeQualifier.precision)); + } + if (!typeQualifier.layoutQualifier.isEmpty()) { - const TString kGlFrontFacing("gl_FrontFacing"); - if (*identifier == kGlFrontFacing) - { - error(identifierLoc, "identifier should not be declared as invariant", - identifier->c_str()); - return nullptr; - } - symbolTable.addInvariantVarying(std::string(identifier->c_str())); - const TVariable *variable = getNamedVariable(identifierLoc, identifier, symbol); - ASSERT(variable); - const TType &type = variable->getType(); - TIntermSymbol *intermSymbol = - intermediate.addSymbol(variable->getUniqueId(), *identifier, type, identifierLoc); - - TIntermAggregate *aggregate = intermediate.makeAggregate(intermSymbol, identifierLoc); - aggregate->setOp(EOpInvariantDeclaration); - return aggregate; + error(identifierLoc, "invariant declaration specifies layout", "'layout'"); } + + const TVariable *variable = getNamedVariable(identifierLoc, identifier, symbol); + ASSERT(variable); + const TType &type = variable->getType(); + + checkInvariantVariableQualifier(typeQualifier.invariant, type.getQualifier(), + typeQualifier.line); + + symbolTable.addInvariantVarying(std::string(identifier->c_str())); + + TIntermSymbol *intermSymbol = + intermediate.addSymbol(variable->getUniqueId(), *identifier, type, identifierLoc); + + TIntermAggregate *aggregate = intermediate.makeAggregate(intermSymbol, identifierLoc); + aggregate->setOp(EOpInvariantDeclaration); + return aggregate; } TIntermAggregate *TParseContext::parseDeclarator(TPublicType &publicType, TIntermAggregate *aggregateDeclaration, const TSourceLoc &identifierLocation, const TString &identifier) { // If the declaration starting this declarator list was empty (example: int,), some checks were @@ -1857,20 +1905,24 @@ TIntermAggregate *TParseContext::parseAr } } else { return nullptr; } } -void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier) +void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder) { + TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics); const TLayoutQualifier layoutQualifier = typeQualifier.layoutQualifier; + checkInvariantVariableQualifier(typeQualifier.invariant, typeQualifier.qualifier, + typeQualifier.line); + // It should never be the case, but some strange parser errors can send us here. if (layoutQualifier.isEmpty()) { error(typeQualifier.line, "Error during layout qualifier parsing.", "?"); return; } if (!layoutQualifier.isCombinationValid()) @@ -2216,17 +2268,18 @@ TFunction *TParseContext::parseFunctionH error(location, "no qualifiers allowed for function return", getQualifierString(type.qualifier)); } if (!type.layoutQualifier.isEmpty()) { error(location, "no qualifiers allowed for function return", "layout"); } // make sure a sampler is not involved as well... - checkIsNotSampler(location, type, "samplers can't be function return values"); + checkIsNotSampler(location, type.typeSpecifierNonArray, + "samplers can't be function return values"); if (mShaderVersion < 300) { // Array return values are forbidden, but there's also no valid syntax for declaring array // return values in ESSL 1.00. ASSERT(type.arraySize == 0 || mDiagnostics.numErrors() > 0); if (type.isStructureContainingArrays()) { @@ -2238,34 +2291,35 @@ TFunction *TParseContext::parseFunctionH // Add the function as a prototype after parsing it (we do not support recursion) return new TFunction(name, new TType(type)); } TFunction *TParseContext::addConstructorFunc(const TPublicType &publicTypeIn) { TPublicType publicType = publicTypeIn; - if (publicType.isStructSpecifier) + if (publicType.isStructSpecifier()) { - error(publicType.line, "constructor can't be a structure definition", - getBasicString(publicType.type)); + error(publicType.getLine(), "constructor can't be a structure definition", + getBasicString(publicType.getBasicType())); } TOperator op = EOpNull; - if (publicType.userDef) + if (publicType.getUserDef()) { op = EOpConstructStruct; } else { op = sh::TypeToConstructorOperator(TType(publicType)); if (op == EOpNull) { - error(publicType.line, "cannot construct this type", getBasicString(publicType.type)); - publicType.type = EbtFloat; + error(publicType.getLine(), "cannot construct this type", + getBasicString(publicType.getBasicType())); + publicType.setBasicType(EbtFloat); op = EOpConstructFloat; } } TString tempString; const TType *type = new TType(publicType); return new TFunction(&tempString, type, op); } @@ -2317,137 +2371,53 @@ TIntermTyped *TParseContext::addConstruc if (op != EOpConstructStruct) { constructor->setPrecisionFromChildren(); type.setPrecision(constructor->getPrecision()); } constructor->setType(type); - TIntermTyped *constConstructor = intermediate.foldAggregateBuiltIn(constructor); + TIntermTyped *constConstructor = intermediate.foldAggregateBuiltIn(constructor, &mDiagnostics); if (constConstructor) { return constConstructor; } return constructor; } -// This function returns vector field(s) being accessed from a constant vector. -TIntermConstantUnion *TParseContext::foldVectorSwizzle(TVectorFields &fields, - TIntermConstantUnion *baseNode, - const TSourceLoc &location) -{ - const TConstantUnion *unionArray = baseNode->getUnionArrayPointer(); - ASSERT(unionArray); - - TConstantUnion *constArray = new TConstantUnion[fields.num]; - const auto &type = baseNode->getType(); - - for (int i = 0; i < fields.num; i++) - { - // Out-of-range indices should already be checked. - ASSERT(fields.offsets[i] < type.getNominalSize()); - constArray[i] = unionArray[fields.offsets[i]]; - } - return intermediate.addConstantUnion(constArray, type, location); -} - -// This function returns the column vector being accessed from a constant matrix. -TIntermConstantUnion *TParseContext::foldMatrixSubscript(int index, - TIntermConstantUnion *baseNode, - const TSourceLoc &location) -{ - ASSERT(index < baseNode->getType().getCols()); - - const TConstantUnion *unionArray = baseNode->getUnionArrayPointer(); - int size = baseNode->getType().getRows(); - return intermediate.addConstantUnion(&unionArray[size * index], baseNode->getType(), location); -} - -// This function returns an element of an array accessed from a constant array. -TIntermConstantUnion *TParseContext::foldArraySubscript(int index, - TIntermConstantUnion *baseNode, - const TSourceLoc &location) -{ - ASSERT(index < static_cast<int>(baseNode->getArraySize())); - - TType arrayElementType = baseNode->getType(); - arrayElementType.clearArrayness(); - size_t arrayElementSize = arrayElementType.getObjectSize(); - const TConstantUnion *unionArray = baseNode->getUnionArrayPointer(); - return intermediate.addConstantUnion(&unionArray[arrayElementSize * index], baseNode->getType(), - location); -} - -// -// This function returns the value of a particular field inside a constant structure from the symbol -// table. -// If there is an embedded/nested struct, it appropriately calls addConstStructNested or -// addConstStructFromAggr function and returns the parse-tree with the values of the embedded/nested -// struct. -// -TIntermTyped *TParseContext::addConstStruct(const TString &identifier, - TIntermTyped *node, - const TSourceLoc &line) -{ - const TFieldList &fields = node->getType().getStruct()->fields(); - size_t instanceSize = 0; - - for (size_t index = 0; index < fields.size(); ++index) - { - if (fields[index]->name() == identifier) - { - break; - } - else - { - instanceSize += fields[index]->type()->getObjectSize(); - } - } - - TIntermTyped *typedNode; - TIntermConstantUnion *tempConstantNode = node->getAsConstantUnion(); - if (tempConstantNode) - { - const TConstantUnion *constArray = tempConstantNode->getUnionArrayPointer(); - - // type will be changed in the calling function - typedNode = intermediate.addConstantUnion(constArray + instanceSize, - tempConstantNode->getType(), line); - } - else - { - error(line, "Cannot offset into the structure", "Error"); - return nullptr; - } - - return typedNode; -} - // // Interface/uniform blocks // -TIntermAggregate *TParseContext::addInterfaceBlock(const TPublicType &typeQualifier, - const TSourceLoc &nameLine, - const TString &blockName, - TFieldList *fieldList, - const TString *instanceName, - const TSourceLoc &instanceLine, - TIntermTyped *arrayIndex, - const TSourceLoc &arrayIndexLine) +TIntermAggregate *TParseContext::addInterfaceBlock( + const TTypeQualifierBuilder &typeQualifierBuilder, + const TSourceLoc &nameLine, + const TString &blockName, + TFieldList *fieldList, + const TString *instanceName, + const TSourceLoc &instanceLine, + TIntermTyped *arrayIndex, + const TSourceLoc &arrayIndexLine) { checkIsNotReserved(nameLine, blockName); + TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics); + if (typeQualifier.qualifier != EvqUniform) { error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier), "interface blocks must be uniform"); } + if (typeQualifier.invariant) + { + error(typeQualifier.line, "invalid qualifier on interface block member", "invariant"); + } + TLayoutQualifier blockLayoutQualifier = typeQualifier.layoutQualifier; checkLocationIsNotSpecified(typeQualifier.line, blockLayoutQualifier); if (blockLayoutQualifier.matrixPacking == EmpUnspecified) { blockLayoutQualifier.matrixPacking = mDefaultMatrixPacking; } @@ -2482,16 +2452,21 @@ TIntermAggregate *TParseContext::addInte case EvqUniform: break; default: error(field->line(), "invalid qualifier on interface block member", getQualifierString(qualifier)); break; } + if (fieldType->isInvariant()) + { + error(field->line(), "invalid qualifier on interface block member", "invariant"); + } + // check layout qualifiers TLayoutQualifier fieldLayoutQualifier = fieldType->getLayoutQualifier(); checkLocationIsNotSpecified(field->line(), fieldLayoutQualifier); if (fieldLayoutQualifier.blockStorage != EbsUnspecified) { error(field->line(), "invalid layout qualifier:", getBlockStorageString(fieldLayoutQualifier.blockStorage), "cannot be used here"); @@ -2625,29 +2600,32 @@ void TParseContext::checkIsBelowStructNe // // Parse an array index expression // TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, const TSourceLoc &location, TIntermTyped *indexExpression) { - TIntermTyped *indexedExpression = NULL; - if (!baseExpression->isArray() && !baseExpression->isMatrix() && !baseExpression->isVector()) { if (baseExpression->getAsSymbolNode()) { error(location, " left of '[' is not of type array, matrix, or vector ", baseExpression->getAsSymbolNode()->getSymbol().c_str()); } else { error(location, " left of '[' is not of type array, matrix, or vector ", "expression"); } + + TConstantUnion *unionArray = new TConstantUnion[1]; + unionArray->setFConst(0.0f); + return intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConst), + location); } TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion(); // TODO(oetuaho@nvidia.com): Get rid of indexConstantUnion == nullptr below once ANGLE is able // to constant fold all constant expressions. Right now we don't allow indexing interface blocks // or fragment outputs with expressions that ANGLE is not able to constant fold, even if the // index is a constant expression. @@ -2667,161 +2645,88 @@ TIntermTyped *TParseContext::addIndexExp else if (mShaderSpec == SH_WEBGL2_SPEC && baseExpression->getQualifier() == EvqFragData) { error(location, "", "[", "array index for gl_FragData must be constant zero"); } } if (indexConstantUnion) { - // If the index is not qualified as constant, the behavior in the spec is undefined. This - // applies even if ANGLE has been able to constant fold it (ANGLE may constant fold - // expressions that are not constant expressions). The most compatible way to handle this - // case is to report a warning instead of an error and force the index to be in the - // correct range. + // If an out-of-range index is not qualified as constant, the behavior in the spec is + // undefined. This applies even if ANGLE has been able to constant fold it (ANGLE may + // constant fold expressions that are not constant expressions). The most compatible way to + // handle this case is to report a warning instead of an error and force the index to be in + // the correct range. bool outOfRangeIndexIsError = indexExpression->getQualifier() == EvqConst; int index = indexConstantUnion->getIConst(0); - if (!baseExpression->isArray()) + + int safeIndex = -1; + + if (baseExpression->isArray()) { - // Array checks are done later because a different error message might be generated - // based on the index in some cases. - if (baseExpression->isVector()) + if (baseExpression->getQualifier() == EvqFragData && index > 0) { - index = checkIndexOutOfRange(outOfRangeIndexIsError, location, index, - baseExpression->getType().getNominalSize(), - "vector field selection out of range", "[]"); + if (mShaderSpec == SH_WEBGL2_SPEC) + { + // Error has been already generated if index is not const. + if (indexExpression->getQualifier() == EvqConst) + { + error(location, "", "[", + "array index for gl_FragData must be constant zero"); + } + safeIndex = 0; + } + else if (!isExtensionEnabled("GL_EXT_draw_buffers")) + { + outOfRangeError(outOfRangeIndexIsError, location, "", "[", + "array index for gl_FragData must be zero when " + "GL_EXT_draw_buffers is disabled"); + safeIndex = 0; + } } - else if (baseExpression->isMatrix()) + // Only do generic out-of-range check if similar error hasn't already been reported. + if (safeIndex < 0) { - index = checkIndexOutOfRange(outOfRangeIndexIsError, location, index, - baseExpression->getType().getCols(), - "matrix field selection out of range", "[]"); + safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index, + baseExpression->getArraySize(), + "array index out of range", "[]"); } } - - TIntermConstantUnion *baseConstantUnion = baseExpression->getAsConstantUnion(); - if (baseConstantUnion) + else if (baseExpression->isMatrix()) { - if (baseExpression->isArray()) - { - index = checkIndexOutOfRange(outOfRangeIndexIsError, location, index, - baseExpression->getArraySize(), - "array index out of range", "[]"); - // Constant folding for array indexing. - indexedExpression = foldArraySubscript(index, baseConstantUnion, location); - } - else if (baseExpression->isVector()) - { - // Constant folding for vector indexing - reusing vector swizzle folding. - TVectorFields fields; - fields.num = 1; - fields.offsets[0] = index; - indexedExpression = foldVectorSwizzle(fields, baseConstantUnion, location); - } - else if (baseExpression->isMatrix()) - { - // Constant folding for matrix indexing. - indexedExpression = foldMatrixSubscript(index, baseConstantUnion, location); - } + safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index, + baseExpression->getType().getCols(), + "matrix field selection out of range", "[]"); } - else + else if (baseExpression->isVector()) { - int safeIndex = -1; - - if (baseExpression->isArray()) - { - if (baseExpression->getQualifier() == EvqFragData && index > 0) - { - if (mShaderSpec == SH_WEBGL2_SPEC) - { - // Error has been already generated if index is not const. - if (indexExpression->getQualifier() == EvqConst) - { - error(location, "", "[", - "array index for gl_FragData must be constant zero"); - } - safeIndex = 0; - } - else if (!isExtensionEnabled("GL_EXT_draw_buffers")) - { - outOfRangeError(outOfRangeIndexIsError, location, "", "[", - "array index for gl_FragData must be zero when " - "GL_EXT_draw_buffers is disabled"); - safeIndex = 0; - } - } - // Only do generic out-of-range check if similar error hasn't already been reported. - if (safeIndex < 0) - { - safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index, - baseExpression->getArraySize(), - "array index out of range", "[]"); - } - } - - // Data of constant unions can't be changed, because it may be shared with other - // constant unions or even builtins, like gl_MaxDrawBuffers. Instead use a new - // sanitized object. - if (safeIndex != -1) - { - TConstantUnion *safeConstantUnion = new TConstantUnion(); - safeConstantUnion->setIConst(safeIndex); - indexConstantUnion->replaceConstantUnion(safeConstantUnion); - } - - indexedExpression = - intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location); + safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index, + baseExpression->getType().getNominalSize(), + "vector field selection out of range", "[]"); } + + ASSERT(safeIndex >= 0); + // Data of constant unions can't be changed, because it may be shared with other + // constant unions or even builtins, like gl_MaxDrawBuffers. Instead use a new + // sanitized object. + if (safeIndex != index) + { + TConstantUnion *safeConstantUnion = new TConstantUnion(); + safeConstantUnion->setIConst(safeIndex); + indexConstantUnion->replaceConstantUnion(safeConstantUnion); + } + + return intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location, + &mDiagnostics); } else { - indexedExpression = - intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location); - } - - if (indexedExpression == 0) - { - TConstantUnion *unionArray = new TConstantUnion[1]; - unionArray->setFConst(0.0f); - indexedExpression = - intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConst), location); - } - else if (baseExpression->isArray()) - { - TType indexedType = baseExpression->getType(); - indexedType.clearArrayness(); - indexedExpression->setType(indexedType); + return intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location, + &mDiagnostics); } - else if (baseExpression->isMatrix()) - { - indexedExpression->setType(TType(baseExpression->getBasicType(), - baseExpression->getPrecision(), EvqTemporary, - static_cast<unsigned char>(baseExpression->getRows()))); - } - else if (baseExpression->isVector()) - { - indexedExpression->setType( - TType(baseExpression->getBasicType(), baseExpression->getPrecision(), EvqTemporary)); - } - else - { - indexedExpression->setType(baseExpression->getType()); - } - - if (baseExpression->getType().getQualifier() == EvqConst && - indexExpression->getType().getQualifier() == EvqConst) - { - indexedExpression->getTypePointer()->setQualifier(EvqConst); - } - else - { - indexedExpression->getTypePointer()->setQualifier(EvqTemporary); - } - - return indexedExpression; } int TParseContext::checkIndexOutOfRange(bool outOfRangeIndexIsError, const TSourceLoc &location, int index, int arraySize, const char *reason, const char *token) @@ -2844,143 +2749,101 @@ int TParseContext::checkIndexOutOfRange( return index; } TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpression, const TSourceLoc &dotLocation, const TString &fieldString, const TSourceLoc &fieldLocation) { - TIntermTyped *indexedExpression = NULL; - if (baseExpression->isArray()) { error(fieldLocation, "cannot apply dot operator to an array", "."); + return baseExpression; } if (baseExpression->isVector()) { TVectorFields fields; if (!parseVectorFields(fieldString, baseExpression->getNominalSize(), fields, fieldLocation)) { fields.num = 1; fields.offsets[0] = 0; } - if (baseExpression->getAsConstantUnion()) - { - // constant folding for vector fields - indexedExpression = - foldVectorSwizzle(fields, baseExpression->getAsConstantUnion(), fieldLocation); - } - else - { - TIntermTyped *index = intermediate.addSwizzle(fields, fieldLocation); - indexedExpression = - intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation); - } - if (indexedExpression == nullptr) - { - indexedExpression = baseExpression; - } - else - { - // Note that the qualifier set here will be corrected later. - indexedExpression->setType(TType(baseExpression->getBasicType(), - baseExpression->getPrecision(), EvqTemporary, - static_cast<unsigned char>(fields.num))); - } + TIntermTyped *index = intermediate.addSwizzle(fields, fieldLocation); + return intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation, + &mDiagnostics); } else if (baseExpression->getBasicType() == EbtStruct) { - bool fieldFound = false; const TFieldList &fields = baseExpression->getType().getStruct()->fields(); if (fields.empty()) { error(dotLocation, "structure has no fields", "Internal Error"); - indexedExpression = baseExpression; + return baseExpression; } else { + bool fieldFound = false; unsigned int i; for (i = 0; i < fields.size(); ++i) { if (fields[i]->name() == fieldString) { fieldFound = true; break; } } if (fieldFound) { - if (baseExpression->getAsConstantUnion()) - { - indexedExpression = addConstStruct(fieldString, baseExpression, dotLocation); - if (indexedExpression == 0) - { - indexedExpression = baseExpression; - } - else - { - indexedExpression->setType(*fields[i]->type()); - } - } - else - { - TConstantUnion *unionArray = new TConstantUnion[1]; - unionArray->setIConst(i); - TIntermTyped *index = intermediate.addConstantUnion( - unionArray, *fields[i]->type(), fieldLocation); - indexedExpression = intermediate.addIndex(EOpIndexDirectStruct, baseExpression, - index, dotLocation); - indexedExpression->setType(*fields[i]->type()); - } + TIntermTyped *index = TIntermTyped::CreateIndexNode(i); + index->setLine(fieldLocation); + return intermediate.addIndex(EOpIndexDirectStruct, baseExpression, index, + dotLocation, &mDiagnostics); } else { error(dotLocation, " no such field in structure", fieldString.c_str()); - indexedExpression = baseExpression; + return baseExpression; } } } else if (baseExpression->isInterfaceBlock()) { - bool fieldFound = false; const TFieldList &fields = baseExpression->getType().getInterfaceBlock()->fields(); if (fields.empty()) { error(dotLocation, "interface block has no fields", "Internal Error"); - indexedExpression = baseExpression; + return baseExpression; } else { + bool fieldFound = false; unsigned int i; for (i = 0; i < fields.size(); ++i) { if (fields[i]->name() == fieldString) { fieldFound = true; break; } } if (fieldFound) { - TConstantUnion *unionArray = new TConstantUnion[1]; - unionArray->setIConst(i); - TIntermTyped *index = - intermediate.addConstantUnion(unionArray, *fields[i]->type(), fieldLocation); - indexedExpression = intermediate.addIndex(EOpIndexDirectInterfaceBlock, - baseExpression, index, dotLocation); - indexedExpression->setType(*fields[i]->type()); + TIntermTyped *index = TIntermTyped::CreateIndexNode(i); + index->setLine(fieldLocation); + return intermediate.addIndex(EOpIndexDirectInterfaceBlock, baseExpression, index, + dotLocation, &mDiagnostics); } else { error(dotLocation, " no such field in interface block", fieldString.c_str()); - indexedExpression = baseExpression; + return baseExpression; } } } else { if (mShaderVersion < 300) { error(dotLocation, " field selection requires structure or vector on left hand side", @@ -2988,29 +2851,18 @@ TIntermTyped *TParseContext::addFieldSel } else { error(dotLocation, " field selection requires structure, vector, or interface block on left hand " "side", fieldString.c_str()); } - indexedExpression = baseExpression; - } - - if (baseExpression->getQualifier() == EvqConst) - { - indexedExpression->getTypePointer()->setQualifier(EvqConst); + return baseExpression; } - else - { - indexedExpression->getTypePointer()->setQualifier(EvqTemporary); - } - - return indexedExpression; } TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType, const TSourceLoc &qualifierTypeLine) { TLayoutQualifier qualifier = TLayoutQualifier::create(); if (qualifierType == "shared") @@ -3078,16 +2930,17 @@ TLayoutQualifier TParseContext::parseLay if (intValue < 0) { error(intValueLine, "out of range:", intValueString.c_str(), "location must be non-negative"); } else { qualifier.location = intValue; + qualifier.locationsSpecified = 1; } } else if (qualifierType == "local_size_x") { parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 0u, &qualifier.localSize); } else if (qualifierType == "local_size_y") @@ -3103,159 +2956,100 @@ TLayoutQualifier TParseContext::parseLay else { error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str()); } return qualifier; } +TTypeQualifierBuilder *TParseContext::createTypeQualifierBuilder(const TSourceLoc &loc) +{ + return new TTypeQualifierBuilder( + new TStorageQualifierWrapper(symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary, loc), + mShaderVersion); +} + TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualifier, TLayoutQualifier rightQualifier, const TSourceLoc &rightQualifierLocation) { - TLayoutQualifier joinedQualifier = leftQualifier; - - if (rightQualifier.location != -1) - { - joinedQualifier.location = rightQualifier.location; - } - if (rightQualifier.matrixPacking != EmpUnspecified) - { - joinedQualifier.matrixPacking = rightQualifier.matrixPacking; - } - if (rightQualifier.blockStorage != EbsUnspecified) - { - joinedQualifier.blockStorage = rightQualifier.blockStorage; - } - - for (size_t i = 0u; i < rightQualifier.localSize.size(); ++i) - { - if (rightQualifier.localSize[i] != -1) - { - if (joinedQualifier.localSize[i] != -1 && - joinedQualifier.localSize[i] != rightQualifier.localSize[i]) - { - error(rightQualifierLocation, - "Cannot have multiple different work group size specifiers", - getWorkGroupSizeString(i)); - } - joinedQualifier.localSize[i] = rightQualifier.localSize[i]; - } - } - - return joinedQualifier; + return sh::JoinLayoutQualifiers(leftQualifier, rightQualifier, rightQualifierLocation, + &mDiagnostics); } -TPublicType TParseContext::joinInterpolationQualifiers(const TSourceLoc &interpolationLoc, - TQualifier interpolationQualifier, - const TSourceLoc &storageLoc, - TQualifier storageQualifier) +TFieldList *TParseContext::addStructDeclaratorListWithQualifiers( + const TTypeQualifierBuilder &typeQualifierBuilder, + TPublicType *typeSpecifier, + TFieldList *fieldList) { - TQualifier mergedQualifier = EvqSmoothIn; - - if (storageQualifier == EvqFragmentIn) + TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics); + + typeSpecifier->qualifier = typeQualifier.qualifier; + typeSpecifier->layoutQualifier = typeQualifier.layoutQualifier; + typeSpecifier->invariant = typeQualifier.invariant; + if (typeQualifier.precision != EbpUndefined) { - if (interpolationQualifier == EvqSmooth) - mergedQualifier = EvqSmoothIn; - else if (interpolationQualifier == EvqFlat) - mergedQualifier = EvqFlatIn; - else - UNREACHABLE(); - } - else if (storageQualifier == EvqCentroidIn) - { - if (interpolationQualifier == EvqSmooth) - mergedQualifier = EvqCentroidIn; - else if (interpolationQualifier == EvqFlat) - mergedQualifier = EvqFlatIn; - else - UNREACHABLE(); + typeSpecifier->precision = typeQualifier.precision; } - else if (storageQualifier == EvqVertexOut) - { - if (interpolationQualifier == EvqSmooth) - mergedQualifier = EvqSmoothOut; - else if (interpolationQualifier == EvqFlat) - mergedQualifier = EvqFlatOut; - else - UNREACHABLE(); - } - else if (storageQualifier == EvqCentroidOut) - { - if (interpolationQualifier == EvqSmooth) - mergedQualifier = EvqCentroidOut; - else if (interpolationQualifier == EvqFlat) - mergedQualifier = EvqFlatOut; - else - UNREACHABLE(); - } - else - { - error(interpolationLoc, - "interpolation qualifier requires a fragment 'in' or vertex 'out' storage qualifier", - getInterpolationString(interpolationQualifier)); - - mergedQualifier = storageQualifier; - } - - TPublicType type; - type.setBasic(EbtVoid, mergedQualifier, storageLoc); - return type; + return addStructDeclaratorList(*typeSpecifier, fieldList); } TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *fieldList) { - checkIsNonVoid(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier.type); - - checkWorkGroupSizeIsNotSpecified(typeSpecifier.line, typeSpecifier.layoutQualifier); + checkPrecisionSpecified(typeSpecifier.getLine(), typeSpecifier.precision, + typeSpecifier.getBasicType()); + + checkIsNonVoid(typeSpecifier.getLine(), (*fieldList)[0]->name(), typeSpecifier.getBasicType()); + + checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), typeSpecifier.layoutQualifier); for (unsigned int i = 0; i < fieldList->size(); ++i) { // // Careful not to replace already known aspects of type, like array-ness // TType *type = (*fieldList)[i]->type(); - type->setBasicType(typeSpecifier.type); - type->setPrimarySize(typeSpecifier.primarySize); - type->setSecondarySize(typeSpecifier.secondarySize); + type->setBasicType(typeSpecifier.getBasicType()); + type->setPrimarySize(typeSpecifier.getPrimarySize()); + type->setSecondarySize(typeSpecifier.getSecondarySize()); type->setPrecision(typeSpecifier.precision); type->setQualifier(typeSpecifier.qualifier); type->setLayoutQualifier(typeSpecifier.layoutQualifier); + type->setInvariant(typeSpecifier.invariant); // don't allow arrays of arrays if (type->isArray()) { - checkIsValidTypeForArray(typeSpecifier.line, typeSpecifier); + checkIsValidTypeForArray(typeSpecifier.getLine(), typeSpecifier); } if (typeSpecifier.array) type->setArraySize(static_cast<unsigned int>(typeSpecifier.arraySize)); - if (typeSpecifier.userDef) + if (typeSpecifier.getUserDef()) { - type->setStruct(typeSpecifier.userDef->getStruct()); + type->setStruct(typeSpecifier.getUserDef()->getStruct()); } - checkIsBelowStructNestingLimit(typeSpecifier.line, *(*fieldList)[i]); + checkIsBelowStructNestingLimit(typeSpecifier.getLine(), *(*fieldList)[i]); } return fieldList; } -TPublicType TParseContext::addStructure(const TSourceLoc &structLine, - const TSourceLoc &nameLine, - const TString *structName, - TFieldList *fieldList) +TTypeSpecifierNonArray TParseContext::addStructure(const TSourceLoc &structLine, + const TSourceLoc &nameLine, + const TString *structName, + TFieldList *fieldList) { TStructure *structure = new TStructure(structName, fieldList); TType *structureType = new TType(structure); // Store a bool in the struct if we're at global scope, to allow us to // skip the local struct scoping workaround in HLSL. - structure->setUniqueId(TSymbolTable::nextUniqueId()); structure->setAtGlobalScope(symbolTable.atGlobalLevel()); if (!structName->empty()) { checkIsNotReserved(nameLine, *structName); TVariable *userTypeDef = new TVariable(structName, *structureType, true); if (!symbolTable.declare(userTypeDef)) { @@ -3273,25 +3067,31 @@ TPublicType TParseContext::addStructure( case EvqGlobal: case EvqTemporary: break; default: error(field.line(), "invalid qualifier on struct member", getQualifierString(qualifier)); break; } + if (field.type()->isInvariant()) + { + error(field.line(), "invalid qualifier on struct member", "invariant"); + } + + checkLocationIsNotSpecified(field.line(), field.type()->getLayoutQualifier()); } - TPublicType publicType; - publicType.setBasic(EbtStruct, EvqTemporary, structLine); - publicType.userDef = structureType; - publicType.isStructSpecifier = true; + TTypeSpecifierNonArray typeSpecifierNonArray; + typeSpecifierNonArray.initialize(EbtStruct, structLine); + typeSpecifierNonArray.userDef = structureType; + typeSpecifierNonArray.isStructSpecifier = true; exitStructDeclaration(); - return publicType; + return typeSpecifierNonArray; } TIntermSwitch *TParseContext::addSwitch(TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &loc) { TBasicType switchType = init->getBasicType(); if ((switchType != EbtInt && switchType != EbtUInt) || init->isMatrix() || init->isArray() || @@ -3406,17 +3206,24 @@ TIntermTyped *TParseContext::createUnary { return nullptr; } // Operators for built-ins are already type checked against their prototype. default: break; } - return intermediate.addUnaryMath(op, child, loc, funcReturnType); + TIntermUnary *node = new TIntermUnary(op, child); + node->setLine(loc); + + TIntermTyped *foldedNode = node->fold(&mDiagnostics); + if (foldedNode) + return foldedNode; + + return node; } TIntermTyped *TParseContext::addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc) { TIntermTyped *node = createUnaryMath(op, child, loc, nullptr); if (node == nullptr) { unaryOpError(loc, GetOperatorString(op), child->getCompleteString()); @@ -4035,17 +3842,18 @@ TIntermTyped *TParseContext::addFunction aggregate->getTypePointer()->setQualifier(EvqConst); } // Some built-in functions have out parameters too. functionCallLValueErrorCheck(fnCandidate, aggregate); // See if we can constant fold a built-in. Note that this may be possible even // if it is not const-qualified. - TIntermTyped *foldedNode = intermediate.foldAggregateBuiltIn(aggregate); + TIntermTyped *foldedNode = + intermediate.foldAggregateBuiltIn(aggregate, &mDiagnostics); if (foldedNode) { callNode = foldedNode; } else { callNode = aggregate; } @@ -4090,44 +3898,45 @@ TIntermTyped *TParseContext::addFunction callNode = intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), loc); } } return callNode; } TIntermTyped *TParseContext::addTernarySelection(TIntermTyped *cond, - TIntermTyped *trueBlock, - TIntermTyped *falseBlock, + TIntermTyped *trueExpression, + TIntermTyped *falseExpression, const TSourceLoc &loc) { checkIsScalarBool(loc, cond); - if (trueBlock->getType() != falseBlock->getType()) + if (trueExpression->getType() != falseExpression->getType()) { - binaryOpError(loc, ":", trueBlock->getCompleteString(), falseBlock->getCompleteString()); - return falseBlock; + binaryOpError(loc, ":", trueExpression->getCompleteString(), + falseExpression->getCompleteString()); + return falseExpression; } // ESSL1 sections 5.2 and 5.7: // ESSL3 section 5.7: // Ternary operator is not among the operators allowed for structures/arrays. - if (trueBlock->isArray() || trueBlock->getBasicType() == EbtStruct) + if (trueExpression->isArray() || trueExpression->getBasicType() == EbtStruct) { error(loc, "ternary operator is not allowed for structures or arrays", ":"); - return falseBlock; + return falseExpression; } // WebGL2 section 5.26, the following results in an error: // "Ternary operator applied to void, arrays, or structs containing arrays" - if (mShaderSpec == SH_WEBGL2_SPEC && trueBlock->getBasicType() == EbtVoid) + if (mShaderSpec == SH_WEBGL2_SPEC && trueExpression->getBasicType() == EbtVoid) { error(loc, "ternary operator is not allowed for void", ":"); - return falseBlock; + return falseExpression; } - return intermediate.addSelection(cond, trueBlock, falseBlock, loc); + return TIntermediate::AddTernarySelection(cond, trueExpression, falseExpression, loc); } // // Parse an array of strings using yyparse. // // Returns 0 for success. // int PaParseStrings(size_t count,
--- a/gfx/angle/src/compiler/translator/ParseContext.h +++ b/gfx/angle/src/compiler/translator/ParseContext.h @@ -6,16 +6,17 @@ #ifndef COMPILER_TRANSLATOR_PARSECONTEXT_H_ #define COMPILER_TRANSLATOR_PARSECONTEXT_H_ #include "compiler/translator/Compiler.h" #include "compiler/translator/Diagnostics.h" #include "compiler/translator/DirectiveHandler.h" #include "compiler/translator/Intermediate.h" #include "compiler/translator/SymbolTable.h" +#include "compiler/translator/QualifierTypes.h" #include "compiler/preprocessor/Preprocessor.h" struct TMatrixFields { bool wholeRow; bool wholeCol; int row; int col; @@ -25,24 +26,23 @@ struct TMatrixFields // 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, - TIntermediate &interm, sh::GLenum type, ShShaderSpec spec, - int options, + ShCompileOptions options, bool checksPrecErrors, TInfoSink &is, const ShBuiltInResources &resources) - : intermediate(interm), + : intermediate(), symbolTable(symt), mDeferredSingleDeclarationErrorCheck(false), mShaderType(type), mShaderSpec(spec), mCompileOptions(options), mShaderVersion(100), mTreeRoot(nullptr), mLoopNestingLevel(0), @@ -62,17 +62,18 @@ class TParseContext : angle::NonCopyable resources.WEBGL_debug_shader_precision == 1), mPreprocessor(&mDiagnostics, &mDirectiveHandler), mScanner(nullptr), mUsesFragData(false), mUsesFragColor(false), mUsesSecondaryOutputs(false), mMinProgramTexelOffset(resources.MinProgramTexelOffset), mMaxProgramTexelOffset(resources.MaxProgramTexelOffset), - mComputeShaderLocalSizeDeclared(false) + mComputeShaderLocalSizeDeclared(false), + mDeclaringFunction(false) { mComputeShaderLocalSize.fill(-1); } const pp::Preprocessor &getPreprocessor() const { return mPreprocessor; } pp::Preprocessor &getPreprocessor() { return mPreprocessor; } void *getScanner() const { return mScanner; } void setScanner(void *scanner) { mScanner = scanner; } @@ -114,16 +115,22 @@ class TParseContext : angle::NonCopyable void decrLoopNestingLevel() { --mLoopNestingLevel; } void incrSwitchNestingLevel() { ++mSwitchNestingLevel; } void decrSwitchNestingLevel() { --mSwitchNestingLevel; } bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; } sh::WorkGroupSize getComputeShaderLocalSize() const; + void enterFunctionDeclaration() { mDeclaringFunction = true; } + + void exitFunctionDeclaration() { mDeclaringFunction = false; } + + bool declaringFunction() const { return mDeclaringFunction; } + // This method is guaranteed to succeed, even if no variable with 'name' exists. const TVariable *getNamedVariable(const TSourceLoc &location, const TString *name, const TSymbol *symbol); TIntermTyped *parseVariableIdentifier(const TSourceLoc &location, const TString *name, const TSymbol *symbol); bool parseVectorFields(const TString&, int vecSize, TVectorFields&, const TSourceLoc &line); @@ -147,39 +154,41 @@ class TParseContext : angle::NonCopyable // Returns a sanitized array size to use (the size is at least 1). unsigned int checkIsValidArraySize(const TSourceLoc &line, TIntermTyped *expr); bool checkIsValidQualifierForArray(const TSourceLoc &line, const TPublicType &elementQualifier); bool checkIsValidTypeForArray(const TSourceLoc &line, const TPublicType &elementType); bool checkIsNonVoid(const TSourceLoc &line, const TString &identifier, const TBasicType &type); void checkIsScalarBool(const TSourceLoc &line, const TIntermTyped *type); void checkIsScalarBool(const TSourceLoc &line, const TPublicType &pType); - bool checkIsNotSampler(const TSourceLoc &line, const TPublicType &pType, const char *reason); + bool checkIsNotSampler(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 checkOutParameterIsNotSampler(const TSourceLoc &line, TQualifier qua