Backed out changeset 542efbbffdd3 (bug 1303443) for bustage
authorIris Hsiao <ihsiao@mozilla.com>
Mon, 14 Nov 2016 11:28:45 +0800
changeset 439956 e9b3898d3366f830ce6acee568a7f68f6fc3eb8c
parent 439955 542efbbffdd313115db6ec60f9a7e691cb4f7f15
child 439957 4e97dee28c6941a78f5ec342fbe95ce5e6a012b1
push id36143
push userjkingston@mozilla.com
push dateWed, 16 Nov 2016 22:20:38 +0000
bugs1303443
milestone51.0a2
backs out542efbbffdd313115db6ec60f9a7e691cb4f7f15
Backed out changeset 542efbbffdd3 (bug 1303443) for bustage
gfx/angle/BUILD.gn
gfx/angle/include/GLSLANG/ShaderLang.h
gfx/angle/include/export.h
gfx/angle/moz.build
gfx/angle/src/commit.h
gfx/angle/src/compiler.gypi
gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp
gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.h
gfx/angle/src/compiler/translator/AddDefaultReturnStatements.cpp
gfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp
gfx/angle/src/compiler/translator/BaseTypes.h
gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h
gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp
gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h
gfx/angle/src/compiler/translator/Compiler.cpp
gfx/angle/src/compiler/translator/Compiler.h
gfx/angle/src/compiler/translator/ConstantUnion.cpp
gfx/angle/src/compiler/translator/ConstantUnion.h
gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp
gfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
gfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp
gfx/angle/src/compiler/translator/Initialize.cpp
gfx/angle/src/compiler/translator/InitializeVariables.cpp
gfx/angle/src/compiler/translator/InitializeVariables.h
gfx/angle/src/compiler/translator/IntermNode.cpp
gfx/angle/src/compiler/translator/IntermNode.h
gfx/angle/src/compiler/translator/IntermNodePatternMatcher.cpp
gfx/angle/src/compiler/translator/IntermNodePatternMatcher.h
gfx/angle/src/compiler/translator/IntermTraverse.cpp
gfx/angle/src/compiler/translator/Intermediate.cpp
gfx/angle/src/compiler/translator/Intermediate.h
gfx/angle/src/compiler/translator/OutputGLSLBase.cpp
gfx/angle/src/compiler/translator/OutputGLSLBase.h
gfx/angle/src/compiler/translator/OutputHLSL.cpp
gfx/angle/src/compiler/translator/OutputHLSL.h
gfx/angle/src/compiler/translator/ParseContext.cpp
gfx/angle/src/compiler/translator/ParseContext.h
gfx/angle/src/compiler/translator/QualifierTypes.cpp
gfx/angle/src/compiler/translator/QualifierTypes.h
gfx/angle/src/compiler/translator/RemoveDynamicIndexing.cpp
gfx/angle/src/compiler/translator/RemovePow.cpp
gfx/angle/src/compiler/translator/RemoveSwitchFallThrough.cpp
gfx/angle/src/compiler/translator/RemoveSwitchFallThrough.h
gfx/angle/src/compiler/translator/RenameFunction.h
gfx/angle/src/compiler/translator/RewriteDoWhile.cpp
gfx/angle/src/compiler/translator/RewriteElseBlocks.cpp
gfx/angle/src/compiler/translator/RewriteTexelFetchOffset.cpp
gfx/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
gfx/angle/src/compiler/translator/SeparateArrayInitialization.cpp
gfx/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp
gfx/angle/src/compiler/translator/ShaderLang.cpp
gfx/angle/src/compiler/translator/SimplifyLoopConditions.cpp
gfx/angle/src/compiler/translator/SplitSequenceOperator.cpp
gfx/angle/src/compiler/translator/SymbolTable.h
gfx/angle/src/compiler/translator/TranslatorESSL.cpp
gfx/angle/src/compiler/translator/TranslatorESSL.h
gfx/angle/src/compiler/translator/TranslatorGLSL.cpp
gfx/angle/src/compiler/translator/TranslatorGLSL.h
gfx/angle/src/compiler/translator/TranslatorHLSL.cpp
gfx/angle/src/compiler/translator/TranslatorHLSL.h
gfx/angle/src/compiler/translator/Types.cpp
gfx/angle/src/compiler/translator/Types.h
gfx/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp
gfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp
gfx/angle/src/compiler/translator/ValidateSwitch.cpp
gfx/angle/src/compiler/translator/ValidateSwitch.h
gfx/angle/src/compiler/translator/depgraph/DependencyGraph.cpp
gfx/angle/src/compiler/translator/depgraph/DependencyGraph.h
gfx/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.cpp
gfx/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h
gfx/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp
gfx/angle/src/compiler/translator/depgraph/DependencyGraphOutput.h
gfx/angle/src/compiler/translator/depgraph/DependencyGraphTraverse.cpp
gfx/angle/src/compiler/translator/glslang.y
gfx/angle/src/compiler/translator/glslang_tab.cpp
gfx/angle/src/compiler/translator/glslang_tab.h
gfx/angle/src/compiler/translator/intermOut.cpp
gfx/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp
gfx/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.h
gfx/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.cpp
gfx/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h
gfx/angle/src/compiler/translator/util.cpp
gfx/angle/src/compiler/translator/util.h
gfx/angle/src/libANGLE/Buffer.cpp
gfx/angle/src/libANGLE/Buffer.h
gfx/angle/src/libANGLE/Context.cpp
gfx/angle/src/libANGLE/Context.h
gfx/angle/src/libANGLE/ImageIndex.cpp
gfx/angle/src/libANGLE/ImageIndex.h
gfx/angle/src/libANGLE/ImageIndexIterator_unittest.cpp
gfx/angle/src/libANGLE/Shader.cpp
gfx/angle/src/libANGLE/formatutils.cpp
gfx/angle/src/libANGLE/moz.build
gfx/angle/src/libANGLE/queryconversions.cpp
gfx/angle/src/libANGLE/queryconversions.h
gfx/angle/src/libANGLE/renderer/BufferImpl.h
gfx/angle/src/libANGLE/renderer/BufferImpl_mock.h
gfx/angle/src/libANGLE/renderer/ShaderImpl.h
gfx/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp
gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp
gfx/angle/src/libANGLE/renderer/d3d/ShaderD3D.cpp
gfx/angle/src/libANGLE/renderer/d3d/ShaderD3D.h
gfx/angle/src/libANGLE/renderer/d3d/WorkaroundsD3D.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d9/Buffer9.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d9/Buffer9.h
gfx/angle/src/libANGLE/renderer/gl/BufferGL.cpp
gfx/angle/src/libANGLE/renderer/gl/BufferGL.h
gfx/angle/src/libANGLE/renderer/gl/RendererGL.cpp
gfx/angle/src/libANGLE/renderer/gl/ShaderGL.cpp
gfx/angle/src/libANGLE/renderer/gl/ShaderGL.h
gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp
gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.h
gfx/angle/src/libANGLE/renderer/gl/WorkaroundsGL.h
gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp
gfx/angle/src/libANGLE/renderer/vulkan/BufferVk.cpp
gfx/angle/src/libANGLE/renderer/vulkan/BufferVk.h
gfx/angle/src/libANGLE/renderer/vulkan/ShaderVk.cpp
gfx/angle/src/libANGLE/renderer/vulkan/ShaderVk.h
gfx/angle/src/libANGLE/validationES.cpp
gfx/angle/src/libANGLE/validationES.h
gfx/angle/src/libANGLE/validationES2.cpp
gfx/angle/src/libANGLE/validationES2.h
gfx/angle/src/libGLESv2/entry_points_gles_2_0.cpp
gfx/angle/src/tests/BUILD.gn
gfx/angle/src/tests/angle_unittests.gypi
gfx/angle/src/tests/compiler_tests/BuiltInFunctionEmulator_test.cpp
gfx/angle/src/tests/compiler_tests/ExpressionLimit_test.cpp
gfx/angle/src/tests/compiler_tests/IntermNode_test.cpp
gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp
gfx/angle/src/tests/compiler_tests/QualificationOrderESSL31_test.cpp
gfx/angle/src/tests/compiler_tests/QualificationOrder_test.cpp
gfx/angle/src/tests/deqp.gypi
gfx/angle/src/tests/deqp_support/angle_deqp_libtester_main.cpp
gfx/angle/src/tests/deqp_support/deqp_gles2_test_expectations.txt
gfx/angle/src/tests/deqp_support/deqp_gles3_test_expectations.txt
gfx/angle/src/tests/gl_tests/D3D11EmulatedIndexedBufferTest.cpp
gfx/angle/src/tests/gl_tests/GLSLTest.cpp
gfx/angle/src/tests/gl_tests/SwizzleTest.cpp
gfx/angle/src/tests/gl_tests/TextureTest.cpp
gfx/angle/src/tests/gl_tests/UniformBufferTest.cpp
gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp
gfx/angle/src/tests/test_utils/ANGLETest.h
gfx/angle/src/tests/test_utils/compiler_test.cpp
gfx/angle/src/tests/test_utils/compiler_test.h
gfx/angle/src/tests/third_party/gpu_test_expectations/HowToMakeChanges.md
gfx/angle/src/tests/third_party/gpu_test_expectations/angle-mods.patch
gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_config.cc
--- a/gfx/angle/BUILD.gn
+++ b/gfx/angle/BUILD.gn
@@ -492,37 +492,33 @@ util_gypi = exec_script("//build/gypi_to
 
 config("angle_util_config") {
   include_dirs = [ "util" ]
   if (is_linux && use_x11) {
     libs = [ "X11" ]
   }
 }
 
-shared_library("angle_util") {
+static_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
@@ -537,34 +533,26 @@ shared_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,28 +44,49 @@ 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 161
+#define ANGLE_SH_VERSION 155
 
 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.
@@ -90,119 +111,132 @@ 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 uint64_t ShCompileOptions;
+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,
 
-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 needed only as a workaround for certain OpenGL driver bugs.
+    SH_EMULATE_BUILT_IN_FUNCTIONS = 0x0100,
 
-// 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 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,
 
-// 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;
+    // 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,
 
-// 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;
+    // 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 bugs in Mac drivers related to do-while by
+    // transforming them into an other construct.
+    SH_REWRITE_DO_WHILE_LOOPS = 0x400000,
 
-// 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;
+    // 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,
 
-// 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;
+    // 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,
 
-// 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;
+    // 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;
 
 // 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
@@ -426,20 +460,21 @@ 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,
-                               ShCompileOptions compileOptions);
+COMPILER_EXPORT bool ShCompile(
+    const ShHandle handle,
+    const char * const shaderStrings[],
+    size_t numStrings,
+    int 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,25 +5,23 @@
 //
 
 // 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) || \
-    defined(LIBANGLE_UTIL_IMPLEMENTATION)
+#   if defined(LIBGLESV2_IMPLEMENTATION) || defined(LIBANGLE_IMPLEMENTATION)
 #       define ANGLE_EXPORT __declspec(dllexport)
 #   else
 #       define ANGLE_EXPORT __declspec(dllimport)
 #   endif
 #elif defined(__GNUC__)
-#if defined(LIBGLESV2_IMPLEMENTATION) || defined(LIBANGLE_IMPLEMENTATION) || \
-    defined(LIBANGLE_UTIL_IMPLEMENTATION)
+#   if defined(LIBGLESV2_IMPLEMENTATION) || defined(LIBANGLE_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,32 +23,33 @@ 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',
@@ -65,17 +66,16 @@ 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,16 +86,18 @@ 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 "8b3e8b4d1b09"
-#define ANGLE_COMMIT_HASH_SIZE 12
-#define ANGLE_COMMIT_DATE "2016-10-24 15:38:47 +0800"
+#define ANGLE_COMMIT_HASH ""
+#define ANGLE_COMMIT_HASH_SIZE 12
+#define ANGLE_COMMIT_DATE ""
--- a/gfx/angle/src/compiler.gypi
+++ b/gfx/angle/src/compiler.gypi
@@ -17,32 +17,27 @@
             '../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',
@@ -81,28 +76,27 @@
             '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',
@@ -119,24 +113,35 @@
             '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',
deleted file mode 100644
--- a/gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-// 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
deleted file mode 100644
--- a/gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.h
+++ /dev/null
@@ -1,20 +0,0 @@
-//
-// 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,23 +49,58 @@ 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, TIntermTyped::CreateZero(returnType));
+                new TIntermBranch(EOpReturn, GenerateTypeConstructor(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,21 +162,22 @@ 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);
-        TIntermSymbol *returnValueSymbol = CreateReturnValueSymbol(expression->getType());
-        TIntermBinary *replacementAssignment =
-            new TIntermBinary(EOpAssign, returnValueSymbol, expression);
+        replacementAssignment->setLeft(CreateReturnValueSymbol(expression->getType()));
+        replacementAssignment->setRight(node->getExpression());
+        replacementAssignment->setType(expression->getType());
         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,21 +336,20 @@ 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
-    EvqCentroid,  // Incomplete qualifier
-    EvqSmoothOut,
-    EvqFlatOut,
+    EvqSmooth,  // Incomplete qualifier, smooth is the default
+    EvqFlat,    // Incomplete qualifier
+    EvqSmoothOut = EvqSmooth,
+    EvqFlatOut   = EvqFlat,
     EvqCentroidOut,  // Implies smooth
     EvqSmoothIn,
     EvqFlatIn,
     EvqCentroidIn,  // Implies smooth
 
     // GLSL ES 3.1 compute shader special variables
     EvqComputeIn,
     EvqNumWorkGroups,
@@ -359,21 +358,16 @@ 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
@@ -382,29 +376,27 @@ 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;
     }
 
@@ -485,19 +477,16 @@ 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";
deleted file mode 100644
--- a/gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-//
-// 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
deleted file mode 100644
--- a/gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h
+++ /dev/null
@@ -1,23 +0,0 @@
-//
-// 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,24 +6,42 @@
 
 #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 InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
-                                                      sh::GLenum shaderType)
+void InitBuiltInFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, sh::GLenum shaderType)
 {
-    if (shaderType == GL_VERTEX_SHADER)
+    // 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)
     {
-        const TType *int1 = TCache::getType(EbtInt);
-        emu->addEmulatedFunction(EOpAbs, int1, "int webgl_abs_emu(int x) { return x * sign(x); }");
+        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); }");
     }
+    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)
@@ -158,19 +176,17 @@ void InitBuiltInFunctionEmulatorForGLSLM
             "            return uintBitsToFloat(sign | 0x7F800000u | mantissa);\n"
             "        }\n"
             "        else\n"
             "        {\n"
             "            exponent -= 15;\n"
             "            float scale;\n"
             "            if(exponent < 0)\n"
             "            {\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"
+            "                scale = 1.0 / (1 << -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,20 +7,19 @@
 #ifndef COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
 #define COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
 
 #include "GLSLANG/ShaderLang.h"
 
 class BuiltInFunctionEmulator;
 
 //
-// This works around bug in Intel Mac drivers.
+// This is only a workaround for OpenGL driver bugs, and isn't needed in general.
 //
-void InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
-                                                      sh::GLenum shaderType);
+void InitBuiltInFunctionEmulatorForGLSLWorkarounds(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,42 +1,47 @@
 //
 // 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_WEBGL2_SPEC || spec == SH_WEBGL3_SPEC);
+    return (spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_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 ||
@@ -50,16 +55,17 @@ 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 {
 
@@ -100,16 +106,17 @@ 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:
@@ -150,17 +157,17 @@ TCompiler::TCompiler(sh::GLenum type, Sh
 {
     mComputeShaderLocalSize.fill(1);
 }
 
 TCompiler::~TCompiler()
 {
 }
 
-bool TCompiler::shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const
+bool TCompiler::shouldRunLoopAndIndexingValidation(int 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);
 }
 
@@ -185,26 +192,25 @@ 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,
-                                              ShCompileOptions compileOptions)
+TIntermNode *TCompiler::compileTreeForTesting(const char* const shaderStrings[],
+    size_t numStrings, int compileOptions)
 {
     return compileTreeImpl(shaderStrings, numStrings, compileOptions);
 }
 
 TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[],
                                         size_t numStrings,
-                                        const ShCompileOptions compileOptions)
+                                        const int compileOptions)
 {
     clearResults();
 
     ASSERT(numStrings > 0);
     ASSERT(GetGlobalPoolAllocator());
 
     // Reset the extension behavior for each compilation unit.
     ResetExtensionBehavior(extensionBehavior);
@@ -212,17 +218,18 @@ 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;
     }
 
-    TParseContext parseContext(symbolTable, extensionBehavior, shaderType, shaderSpec,
+    TIntermediate intermediate(infoSink);
+    TParseContext parseContext(symbolTable, extensionBehavior, intermediate, 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);
@@ -246,17 +253,17 @@ TIntermNode *TCompiler::compileTreeImpl(
     {
         mPragma = parseContext.pragma();
         symbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll);
 
         mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared();
         mComputeShaderLocalSize         = parseContext.getComputeShaderLocalSize();
 
         root = parseContext.getTreeRoot();
-        root = TIntermediate::PostProcess(root);
+        root = intermediate.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);
 
@@ -283,16 +290,22 @@ 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))
@@ -327,19 +340,16 @@ 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))
@@ -393,24 +403,22 @@ TIntermNode *TCompiler::compileTreeImpl(
 
     SetGlobalParseContext(NULL);
     if (success)
         return root;
 
     return NULL;
 }
 
-bool TCompiler::compile(const char *const shaderStrings[],
-                        size_t numStrings,
-                        ShCompileOptions compileOptionsIn)
+bool TCompiler::compile(const char *const shaderStrings[], size_t numStrings, int compileOptionsIn)
 {
     if (numStrings == 0)
         return true;
 
-    ShCompileOptions compileOptions = compileOptionsIn;
+    int 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;
     }
 
@@ -439,23 +447,25 @@ 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.setBasicType(EbtInt);
-    integer.initializeSizeForScalarTypes();
+    integer.type = EbtInt;
+    integer.primarySize = 1;
+    integer.secondarySize = 1;
     integer.array = false;
 
     TPublicType floatingPoint;
-    floatingPoint.setBasicType(EbtFloat);
-    floatingPoint.initializeSizeForScalarTypes();
+    floatingPoint.type = EbtFloat;
+    floatingPoint.primarySize = 1;
+    floatingPoint.secondarySize = 1;
     floatingPoint.array = false;
 
     switch(shaderType)
     {
       case GL_FRAGMENT_SHADER:
         symbolTable.setDefaultPrecision(integer, EbpMedium);
         break;
       case GL_VERTEX_SHADER:
@@ -485,19 +495,20 @@ bool TCompiler::InitBuiltInSymbolTable(c
 
     return true;
 }
 
 void TCompiler::initSamplerDefaultPrecision(TBasicType samplerType)
 {
     ASSERT(samplerType > EbtGuardSamplerBegin && samplerType < EbtGuardSamplerEnd);
     TPublicType sampler;
-    sampler.initializeSizeForScalarTypes();
-    sampler.setBasicType(samplerType);
+    sampler.primarySize   = 1;
+    sampler.secondarySize = 1;
     sampler.array         = false;
+    sampler.type          = samplerType;
     symbolTable.setDefaultPrecision(sampler, EbpLow);
 }
 
 void TCompiler::setResourceString()
 {
     std::ostringstream strstream;
 
     // clang-format off
@@ -747,23 +758,59 @@ 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.";
@@ -774,16 +821,30 @@ 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);
 
@@ -854,17 +915,17 @@ ShArrayIndexClampingStrategy TCompiler::
     return clampingStrategy;
 }
 
 const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
 {
     return builtInFunctionEmulator;
 }
 
-void TCompiler::writePragma(ShCompileOptions compileOptions)
+void TCompiler::writePragma(int 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,22 +20,24 @@
 #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.
+// Helper function to identify specs that are based on the WebGL spec,
+// like the CSS Shaders spec.
 //
 bool IsWebGLBasedSpec(ShShaderSpec spec);
 
 //
 // Helper function to check if the shader type is GLSL.
 //
 bool IsGLSL130OrNewer(ShShaderOutput output);
 
@@ -68,23 +70,21 @@ 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,
-                                       ShCompileOptions compileOptions);
+    TIntermNode *compileTreeForTesting(const char* const shaderStrings[],
+        size_t numStrings, int compileOptions);
 
-    bool compile(const char *const shaderStrings[],
-                 size_t numStrings,
-                 ShCompileOptions compileOptions);
+    bool compile(const char* const shaderStrings[],
+        size_t numStrings, int 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,68 +99,76 @@ 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(ShCompileOptions compileOptions) const;
+    bool shouldRunLoopAndIndexingValidation(int 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,
-                                             ShCompileOptions compileOptions){};
+    virtual void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions) {};
     // Translate to object code.
-    virtual void translate(TIntermNode *root, ShCompileOptions compileOptions) = 0;
+    virtual void translate(TIntermNode *root, int 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(ShCompileOptions compileOptions);
+    void writePragma(int 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(ShCompileOptions compileOptions)
+    virtual bool shouldCollectVariables(int compileOptions)
     {
         return (compileOptions & SH_VARIABLES) != 0;
     }
 
     virtual bool shouldFlattenPragmaStdglInvariantAll() = 0;
 
     std::vector<sh::Attribute> attributes;
     std::vector<sh::OutputVariable> outputVariables;
@@ -180,17 +188,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 ShCompileOptions compileOptions);
+                                 const int compileOptions);
 
     sh::GLenum shaderType;
     ShShaderSpec shaderSpec;
     ShShaderOutput outputType;
 
     struct FunctionMetadata
     {
         FunctionMetadata()
deleted file mode 100644
--- a/gfx/angle/src/compiler/translator/ConstantUnion.cpp
+++ /dev/null
@@ -1,443 +0,0 @@
-//
-// 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,74 +4,345 @@
 // 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 TDiagnostics;
+class TConstantUnion {
+public:
+    POOL_ALLOCATOR_NEW_DELETE();
+    TConstantUnion()
+    {
+        iConst = 0;
+        type = EbtVoid;
+    }
 
-class TConstantUnion
-{
-  public:
-    POOL_ALLOCATOR_NEW_DELETE();
-    TConstantUnion();
+    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;
+        }
 
-    bool cast(TBasicType newType, const TConstantUnion &constant);
+        return true;
+    }
 
     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;
-    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;
+    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;
+    }
 
     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,18 +96,20 @@ 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, symbolNode->deepCopy(), node->getRight());
+            TIntermBinary *deferredInit = new TIntermBinary(EOpAssign);
+            deferredInit->setLeft(symbolNode->deepCopy());
+            deferredInit->setRight(node->getRight());
+            deferredInit->setType(node->getType());
             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,64 +12,66 @@
 //
 
 #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(int maxDrawBuffers)
-        : TIntermTraverser(true, false, false),
-          mMainSequence(nullptr),
-          mGLFragColorUsed(false),
-          mMaxDrawBuffers(maxDrawBuffers)
+    GLFragColorBroadcastTraverser()
+        : TIntermTraverser(true, false, false), mMainSequence(nullptr), mGLFragColorUsed(false)
     {
     }
 
-    void broadcastGLFragColor();
+    void broadcastGLFragColor(int maxDrawBuffers);
 
     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;
     }
 }
@@ -94,47 +96,47 @@ bool GLFragColorBroadcastTraverser::visi
             }
             break;
         default:
             break;
     }
     return true;
 }
 
-void GLFragColorBroadcastTraverser::broadcastGLFragColor()
+void GLFragColorBroadcastTraverser::broadcastGLFragColor(int maxDrawBuffers)
 {
-    ASSERT(mMaxDrawBuffers > 1);
+    ASSERT(maxDrawBuffers > 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 < mMaxDrawBuffers; ++colorIndex)
+    for (int colorIndex = 1; colorIndex < maxDrawBuffers; ++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(maxDrawBuffers);
+    GLFragColorBroadcastTraverser traverser;
     root->traverse(&traverser);
     if (traverser.isGLFragColorUsed())
     {
         traverser.updateTree();
-        traverser.broadcastGLFragColor();
+        traverser.broadcastGLFragColor(maxDrawBuffers);
         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,28 +119,33 @@ 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, current, createTempSymbol(lhs->getType()));
+        TIntermBinary *mul = new TIntermBinary(EOpMul);
+        mul->setLeft(current);
+        mul->setRight(createTempSymbol(lhs->getType()));
+        mul->setType(node->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, oneNode, current);
+        TIntermBinary *div            = new TIntermBinary(EOpDiv);
+        div->setLeft(oneNode);
+        div->setRight(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,23 +506,26 @@ 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);
 
-    symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxDrawBuffers", resources.MaxDrawBuffers,
-                               EbpMedium);
-    if (resources.EXT_blend_func_extended)
+    if (spec != SH_CSS_SHADERS_SPEC)
     {
-        symbolTable.insertConstIntExt(COMMON_BUILTINS, "GL_EXT_blend_func_extended",
-                                      "gl_MaxDualSourceDrawBuffersEXT",
-                                      resources.MaxDualSourceDrawBuffers);
+        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.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);
@@ -582,83 +585,95 @@ 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)));
 
-          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));
+        //
+        // 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));
 
-          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)));
-          }
-      }
+                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)));
+        }
 
         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,16 +9,56 @@
 #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)
     {
     }
 
@@ -75,53 +115,83 @@ bool VariableInitializer::visitAggregate
         visitChildren = false;
         break;
     }
     return visitChildren;
 }
 
 void VariableInitializer::insertInitCode(TIntermSequence *sequence)
 {
-    for (const auto &var : mVariables)
+    for (size_t ii = 0; ii < mVariables.size(); ++ii)
     {
+        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);
-            }
-            TType elementType = type;
-            elementType.clearArrayness();
-
-            for (unsigned int i = 0; i < var.arraySize; ++i)
+            for (int index = static_cast<int>(var.arraySize) - 1; index >= 0; --index)
             {
-                TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, type);
-                TIntermBinary *element     = new TIntermBinary(EOpIndexDirect, arraySymbol,
-                                                           TIntermTyped::CreateIndexNode(i));
+                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);
 
-                TIntermTyped *zero        = TIntermTyped::CreateZero(elementType);
-                TIntermBinary *assignment = new TIntermBinary(EOpAssign, element, zero);
+                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)
+            {
+                TIntermBinary *assign = new TIntermBinary(EOpAssign);
+                sequence->insert(sequence->begin(), assign);
 
-                sequence->insert(sequence->begin(), assignment);
+                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);
+
+                const sh::ShaderVariable &field = var.fields[fieldIndex];
+                TType fieldType                 = sh::ConvertShaderVariableTypeToTType(field.type);
+                TIntermConstantUnion *zeroConst = constructConstUnionNode(fieldType);
+                assign->setRight(zeroConst);
             }
         }
         else
         {
+            TType type            = sh::ConvertShaderVariableTypeToTType(var.type);
+            TIntermBinary *assign = new TIntermBinary(EOpAssign);
+            sequence->insert(sequence->begin(), assign);
             TIntermSymbol *symbol = new TIntermSymbol(0, name, type);
-            TIntermTyped *zero    = TIntermTyped::CreateZero(type);
+            assign->setLeft(symbol);
+            TIntermConstantUnion *zeroConst = constructConstUnionNode(type);
+            assign->setRight(zeroConst);
+        }
 
-            TIntermBinary *assign = new TIntermBinary(EOpAssign, symbol, zero);
-            sequence->insert(sequence->begin(), assign);
-        }
     }
 }
 
 }  // 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,12 +8,11 @@
 #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,17 +16,16 @@
 #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;
 
@@ -39,24 +38,23 @@ 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,
-                                   TDiagnostics *diagnostics,
-                                   TConstantUnion *result)
+void UndefinedConstantFoldingError(const TSourceLoc &loc, TOperator op, TBasicType basicType,
+                                   TInfoSink &infoSink, TConstantUnion *result)
 {
-    diagnostics->warning(loc, "operation result is undefined for the values passed in",
-                         GetOperatorString(op), "");
+    std::stringstream constantFoldingErrorStream;
+    constantFoldingErrorStream << "'" << GetOperatorString(op)
+                               << "' operation result is undefined for the values passed in";
+    infoSink.info.message(EPrefixWarning, loc, constantFoldingErrorStream.str().c_str());
 
     switch (basicType)
     {
       case EbtFloat :
         result->setFConst(0.0f);
         break;
       case EbtInt:
         result->setIConst(0);
@@ -88,17 +86,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(const TConstantUnion *constArray,
+TIntermTyped *CreateFoldedNode(TConstantUnion *constArray,
                                const TIntermTyped *originalNode,
                                TQualifier qualifier)
 {
     if (constArray == nullptr)
     {
         return nullptr;
     }
     TIntermTyped *folded = new TIntermConstantUnion(constArray, originalNode->getType());
@@ -187,17 +185,16 @@ 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)
@@ -287,24 +284,16 @@ 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;
 }
@@ -342,93 +331,16 @@ 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),
@@ -459,25 +371,30 @@ TIntermBinary::TIntermBinary(const TInte
 TIntermUnary::TIntermUnary(const TIntermUnary &node)
     : TIntermOperator(node), mUseEmulatedFunction(node.mUseEmulatedFunction)
 {
     TIntermTyped *operandCopy = node.mOperand->deepCopy();
     ASSERT(operandCopy != nullptr);
     mOperand = operandCopy;
 }
 
-TIntermTernary::TIntermTernary(const TIntermTernary &node) : TIntermTyped(node)
+TIntermSelection::TIntermSelection(const TIntermSelection &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      = node.mTrueExpression->deepCopy();
-    TIntermTyped *falseCopy     = node.mFalseExpression->deepCopy();
+    TIntermTyped *trueCopy      = trueTyped->deepCopy();
+    TIntermTyped *falseCopy = falseTyped->deepCopy();
     ASSERT(conditionCopy != nullptr && trueCopy != nullptr && falseCopy != nullptr);
-    mCondition       = conditionCopy;
-    mTrueExpression  = trueCopy;
-    mFalseExpression = falseCopy;
+    mCondition  = conditionCopy;
+    mTrueBlock  = trueCopy;
+    mFalseBlock = falseCopy;
 }
 
 bool TIntermOperator::isAssignment() const
 {
     return IsAssignment(mOp);
 }
 
 bool TIntermOperator::isMultiplication() const
@@ -622,190 +539,96 @@ TOperator TIntermBinary::GetMulAssignOpB
         }
     }
 }
 
 //
 // Make sure the type of a unary operator is appropriate for its
 // combination of operation and operand type.
 //
-void TIntermUnary::promote()
+void TIntermUnary::promote(const TType *funcReturnType)
 {
-    TQualifier resultQualifier = EvqTemporary;
-    if (mOperand->getQualifier() == EvqConst)
-        resultQualifier = EvqConst;
-
-    unsigned char operandPrimarySize =
-        static_cast<unsigned char>(mOperand->getType().getNominalSize());
     switch (mOp)
     {
-        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;
+      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());
     }
-}
 
-TIntermUnary::TIntermUnary(TOperator op, TIntermTyped *operand)
-    : TIntermOperator(op), mOperand(operand), mUseEmulatedFunction(false)
-{
-    promote();
+    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);
 }
 
 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)
     {
@@ -815,27 +638,27 @@ void TIntermBinary::promote()
           // Promote to conditional
           //
           case EOpEqual:
           case EOpNotEqual:
           case EOpLessThan:
           case EOpGreaterThan:
           case EOpLessThanEqual:
           case EOpGreaterThanEqual:
-              setType(TType(EbtBool, EbpUndefined, resultQualifier));
-              break;
+            setType(TType(EbtBool, EbpUndefined));
+            break;
 
           //
           // And and Or operate on conditionals
           //
           case EOpLogicalAnd:
           case EOpLogicalXor:
           case EOpLogicalOr:
             ASSERT(mLeft->getBasicType() == EbtBool && mRight->getBasicType() == EbtBool);
-            setType(TType(EbtBool, EbpUndefined, resultQualifier));
+            setType(TType(EbtBool, EbpUndefined));
             break;
 
           default:
             break;
         }
         return;
     }
 
@@ -921,126 +744,46 @@ void TIntermBinary::promote()
                    (mLeft->getSecondarySize() == mRight->getSecondarySize()));
             setType(TType(EbtBool, EbpUndefined, resultQualifier));
             break;
 
         case EOpIndexDirect:
         case EOpIndexIndirect:
         case EOpIndexDirectInterfaceBlock:
         case EOpIndexDirectStruct:
-        case EOpVectorSwizzle:
-            // These ops should be already fully handled.
+            // TODO (oetuaho): These ops could be handled here as well (should be done closer to the
+            // top of the function).
             UNREACHABLE();
             break;
         default:
             UNREACHABLE();
             break;
     }
 }
 
-const TConstantUnion *TIntermConstantUnion::foldIndexing(int index)
-{
-    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;
-    }
-}
-
 TIntermTyped *TIntermBinary::fold(TDiagnostics *diagnostics)
 {
-    TIntermConstantUnion *leftConstant  = mLeft->getAsConstantUnion();
+    TIntermConstantUnion *leftConstant = mLeft->getAsConstantUnion();
     TIntermConstantUnion *rightConstant = mRight->getAsConstantUnion();
-    switch (mOp)
+    if (leftConstant == nullptr || rightConstant == nullptr)
     {
-        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();
-            }
+        return nullptr;
+    }
+    TConstantUnion *constArray = leftConstant->foldBinary(mOp, rightConstant, diagnostics);
 
-            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());
-        }
+    // 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(TDiagnostics *diagnostics)
+TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink)
 {
     TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion();
     if (operandConstant == nullptr)
     {
         return nullptr;
     }
 
     TConstantUnion *constArray = nullptr;
@@ -1053,42 +796,43 @@ TIntermTyped *TIntermUnary::fold(TDiagno
       case EOpDeterminant:
       case EOpInverse:
       case EOpPackSnorm2x16:
       case EOpUnpackSnorm2x16:
       case EOpPackUnorm2x16:
       case EOpUnpackUnorm2x16:
       case EOpPackHalf2x16:
       case EOpUnpackHalf2x16:
-          constArray = operandConstant->foldUnaryNonComponentWise(mOp);
-          break;
+        constArray = operandConstant->foldUnaryWithDifferentReturnType(mOp, infoSink);
+        break;
       default:
-          constArray = operandConstant->foldUnaryComponentWise(mOp, diagnostics);
-          break;
+        constArray = operandConstant->foldUnaryWithSameReturnType(mOp, infoSink);
+        break;
     }
 
     // Nodes may be constant folded without being qualified as constant.
-    return CreateFoldedNode(constArray, this, mType.getQualifier());
+    TQualifier resultQualifier = mOperand->getQualifier() == EvqConst ? EvqConst : EvqTemporary;
+    return CreateFoldedNode(constArray, this, resultQualifier);
 }
 
-TIntermTyped *TIntermAggregate::fold(TDiagnostics *diagnostics)
+TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink)
 {
     // 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);
+        constArray = TIntermConstantUnion::FoldAggregateConstructor(this, infoSink);
     else
-        constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, diagnostics);
+        constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, infoSink);
 
     // 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,
@@ -1098,17 +842,20 @@ TIntermTyped *TIntermAggregate::fold(TDi
 //
 TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op,
                                                  TIntermConstantUnion *rightNode,
                                                  TDiagnostics *diagnostics)
 {
     const TConstantUnion *leftArray  = getUnionArrayPointer();
     const TConstantUnion *rightArray = rightNode->getUnionArrayPointer();
 
-    ASSERT(leftArray && rightArray);
+    if (!leftArray)
+        return nullptr;
+    if (!rightArray)
+        return nullptr;
 
     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);
     }
@@ -1121,30 +868,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] = TConstantUnion::add(leftArray[i], rightArray[i], diagnostics);
+            resultArray[i] = leftArray[i] + rightArray[i];
         break;
       case EOpSub:
         resultArray = new TConstantUnion[objectSize];
         for (size_t i = 0; i < objectSize; i++)
-            resultArray[i] = TConstantUnion::sub(leftArray[i], rightArray[i], diagnostics);
+            resultArray[i] = leftArray[i] - rightArray[i];
         break;
 
       case EOpMul:
       case EOpVectorTimesScalar:
       case EOpMatrixTimesScalar:
         resultArray = new TConstantUnion[objectSize];
         for (size_t i = 0; i < objectSize; i++)
-            resultArray[i] = TConstantUnion::mul(leftArray[i], rightArray[i], diagnostics);
+            resultArray[i] = leftArray[i] * rightArray[i];
         break;
 
       case EOpMatrixTimesMatrix:
         {
             ASSERT(getType().getBasicType() == EbtFloat && rightNode->getBasicType() == EbtFloat);
 
             const int leftCols = getCols();
             const int leftRows = getRows();
@@ -1395,539 +1142,690 @@ TConstantUnion *TIntermConstantUnion::fo
 
       default:
           UNREACHABLE();
           return nullptr;
     }
     return resultArray;
 }
 
-// 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)
+//
+// 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)
 {
-    // Do operations where the return type may have a different number of components compared to the
-    // operand type.
+    //
+    // Do operations where the return type has a different number of components compared to the operand type.
+    //
 
     const TConstantUnion *operandArray = getUnionArrayPointer();
-    ASSERT(operandArray);
+    if (!operandArray)
+        return nullptr;
 
     size_t objectSize = getType().getObjectSize();
     TConstantUnion *resultArray = nullptr;
     switch (op)
     {
-        case EOpAny:
-            ASSERT(getType().getBasicType() == EbtBool);
+      case EOpAny:
+        if (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:
-            ASSERT(getType().getBasicType() == EbtBool);
+      case EOpAll:
+        if (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:
-            ASSERT(getType().getBasicType() == EbtFloat);
+      case EOpLength:
+        if (getType().getBasicType() == EbtFloat)
+        {
             resultArray = new TConstantUnion();
             resultArray->setFConst(VectorLength(operandArray, objectSize));
             break;
+        }
+        else
+        {
+            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
+            return nullptr;
+        }
 
-        case EOpTranspose:
+      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
+        {
+            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
+            return nullptr;
+        }
 
-        case EOpDeterminant:
+      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
+        {
+            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
+            return nullptr;
+        }
 
-        case EOpInverse:
+      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:
-            ASSERT(getType().getBasicType() == EbtFloat);
+      case EOpPackSnorm2x16:
+        if (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
+        {
+            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
+            return nullptr;
+        }
 
-        case EOpUnpackSnorm2x16:
+      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:
-            ASSERT(getType().getBasicType() == EbtFloat);
+      case EOpPackUnorm2x16:
+        if (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
+        {
+            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
+            return nullptr;
+        }
 
-        case EOpUnpackUnorm2x16:
+      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:
-            ASSERT(getType().getBasicType() == EbtFloat);
+      case EOpPackHalf2x16:
+        if (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
+        {
+            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
+            return nullptr;
+        }
 
-        case EOpUnpackHalf2x16:
+      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:
-            UNREACHABLE();
-            break;
+      default:
+        break;
     }
 
     return resultArray;
 }
 
-TConstantUnion *TIntermConstantUnion::foldUnaryComponentWise(TOperator op,
-                                                             TDiagnostics *diagnostics)
+TConstantUnion *TIntermConstantUnion::foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink)
 {
-    // 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.
+    //
+    // Do unary operations where the return type is the same as operand type.
+    //
 
     const TConstantUnion *operandArray = getUnionArrayPointer();
-    ASSERT(operandArray);
+    if (!operandArray)
+        return nullptr;
 
     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:
-                        UNREACHABLE();
-                        return nullptr;
-                }
+          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())));
-                        break;
-                    default:
-                        UNREACHABLE();
-                        return nullptr;
-                }
+          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:
+                infoSink.info.message(
+                    EPrefixInternalError, getLine(),
+                    "Unary operation not folded into constant");
+                return nullptr;
+            }
+            break;
 
-            case EOpLogicalNot:
-                switch (getType().getBasicType())
-                {
-                    case EbtBool:
-                        resultArray[i].setBConst(!operandArray[i].getBConst());
-                        break;
-                    default:
-                        UNREACHABLE();
-                        return nullptr;
-                }
+          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());
                 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());
-                        break;
-                    case EbtUInt:
-                        resultArray[i].setUConst(~operandArray[i].getUConst());
-                        break;
-                    default:
-                        UNREACHABLE();
-                        return nullptr;
-                }
+          case EOpBitwiseNot:
+            switch (getType().getBasicType())
+            {
+              case EbtInt:
+                resultArray[i].setIConst(~operandArray[i].getIConst());
+                break;
+              case EbtUInt:
+                resultArray[i].setUConst(~operandArray[i].getUConst());
                 break;
+              default:
+                infoSink.info.message(
+                    EPrefixInternalError, getLine(),
+                    "Unary operation not folded into constant");
+                return nullptr;
+            }
+            break;
 
-            case EOpRadians:
-                ASSERT(getType().getBasicType() == EbtFloat);
+          case EOpRadians:
+            if (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:
-                ASSERT(getType().getBasicType() == EbtFloat);
+          case EOpDegrees:
+            if (getType().getBasicType() == EbtFloat)
+            {
                 resultArray[i].setFConst(kRadiansToDegreesMultiplier * operandArray[i].getFConst());
                 break;
-
-            case EOpSin:
-                foldFloatTypeUnary(operandArray[i], &sinf, &resultArray[i]);
-                break;
+            }
+            infoSink.info.message(
+                EPrefixInternalError, getLine(),
+                "Unary operation not folded into constant");
+            return nullptr;
 
-            case EOpCos:
-                foldFloatTypeUnary(operandArray[i], &cosf, &resultArray[i]);
-                break;
+          case EOpSin:
+            if (!foldFloatTypeUnary(operandArray[i], &sinf, infoSink, &resultArray[i]))
+               return nullptr;
+            break;
 
-            case EOpTan:
-                foldFloatTypeUnary(operandArray[i], &tanf, &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 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 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 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 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 EOpAtan:
-                foldFloatTypeUnary(operandArray[i], &atanf, &resultArray[i]);
-                break;
+          case EOpAtan:
+            if (!foldFloatTypeUnary(operandArray[i], &atanf, infoSink, &resultArray[i]))
+                return nullptr;
+            break;
 
-            case EOpSinh:
-                foldFloatTypeUnary(operandArray[i], &sinhf, &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 EOpCosh:
-                foldFloatTypeUnary(operandArray[i], &coshf, &resultArray[i]);
-                break;
+          case EOpTanh:
+            if (!foldFloatTypeUnary(operandArray[i], &tanhf, infoSink, &resultArray[i]))
+                return nullptr;
+            break;
 
-            case EOpTanh:
-                foldFloatTypeUnary(operandArray[i], &tanhf, &resultArray[i]);
-                break;
+          case EOpAsinh:
+            if (!foldFloatTypeUnary(operandArray[i], &asinhf, infoSink, &resultArray[i]))
+                return nullptr;
+            break;
 
-            case EOpAsinh:
-                foldFloatTypeUnary(operandArray[i], &asinhf, &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 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 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 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]);
+          case EOpAbs:
+            switch (getType().getBasicType())
+            {
+              case EbtFloat:
+                resultArray[i].setFConst(fabsf(operandArray[i].getFConst()));
                 break;
+              case EbtInt:
+                resultArray[i].setIConst(abs(operandArray[i].getIConst()));
+                break;
+              default:
+                infoSink.info.message(
+                    EPrefixInternalError, getLine(),
+                    "Unary operation not folded into constant");
+                return nullptr;
+            }
+            break;
 
-            case EOpAbs:
-                switch (getType().getBasicType())
+          case EOpSign:
+            switch (getType().getBasicType())
+            {
+              case EbtFloat:
                 {
-                    case EbtFloat:
-                        resultArray[i].setFConst(fabsf(operandArray[i].getFConst()));
-                        break;
-                    case EbtInt:
-                        resultArray[i].setIConst(abs(operandArray[i].getIConst()));
-                        break;
-                    default:
-                        UNREACHABLE();
-                        return nullptr;
+                    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 EOpSign:
-                switch (getType().getBasicType())
+              case EbtInt:
                 {
-                    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;
+                    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:
+                infoSink.info.message(
+                    EPrefixInternalError, getLine(),
+                    "Unary operation not folded into constant");
+                return nullptr;
+            }
+            break;
 
-            case EOpFloor:
-                foldFloatTypeUnary(operandArray[i], &floorf, &resultArray[i]);
-                break;
+          case EOpFloor:
+            if (!foldFloatTypeUnary(operandArray[i], &floorf, infoSink, &resultArray[i]))
+                return nullptr;
+            break;
 
-            case EOpTrunc:
-                foldFloatTypeUnary(operandArray[i], &truncf, &resultArray[i]);
-                break;
+          case EOpTrunc:
+            if (!foldFloatTypeUnary(operandArray[i], &truncf, infoSink, &resultArray[i]))
+                return nullptr;
+            break;
 
-            case EOpRound:
-                foldFloatTypeUnary(operandArray[i], &roundf, &resultArray[i]);
-                break;
+          case EOpRound:
+            if (!foldFloatTypeUnary(operandArray[i], &roundf, infoSink, &resultArray[i]))
+                return nullptr;
+            break;
 
-            case EOpRoundEven:
+          case EOpRoundEven:
+            if (getType().getBasicType() == EbtFloat)
             {
-                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:
-                foldFloatTypeUnary(operandArray[i], &ceilf, &resultArray[i]);
-                break;
+          case EOpCeil:
+            if (!foldFloatTypeUnary(operandArray[i], &ceilf, infoSink, &resultArray[i]))
+                return nullptr;
+            break;
 
-            case EOpFract:
+          case EOpFract:
+            if (getType().getBasicType() == EbtFloat)
             {
-                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:
-                ASSERT(getType().getBasicType() == EbtFloat);
+          case EOpIsNan:
+            if (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:
-                ASSERT(getType().getBasicType() == EbtFloat);
+          case EOpIsInf:
+            if (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:
-                ASSERT(getType().getBasicType() == EbtFloat);
+          case EOpFloatBitsToInt:
+            if (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:
-                ASSERT(getType().getBasicType() == EbtFloat);
+          case EOpFloatBitsToUint:
+            if (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:
-                ASSERT(getType().getBasicType() == EbtInt);
+          case EOpIntBitsToFloat:
+            if (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:
-                ASSERT(getType().getBasicType() == EbtUInt);
+          case EOpUintBitsToFloat:
+            if (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:
-                foldFloatTypeUnary(operandArray[i], &expf, &resultArray[i]);
-                break;
+          case EOpExp:
+            if (!foldFloatTypeUnary(operandArray[i], &expf, 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 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 EOpExp2:
-                foldFloatTypeUnary(operandArray[i], &exp2f, &resultArray[i]);
-                break;
+          case EOpExp2:
+            if (!foldFloatTypeUnary(operandArray[i], &exp2f, infoSink, &resultArray[i]))
+                return nullptr;
+            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 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 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 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 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 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 EOpVectorLogicalNot:
-                ASSERT(getType().getBasicType() == EbtBool);
+          case EOpVectorLogicalNot:
+            if (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:
+          case EOpNormalize:
+            if (getType().getBasicType() == EbtFloat)
             {
-                ASSERT(getType().getBasicType() == EbtFloat);
-                float x      = operandArray[i].getFConst();
+                float x = operandArray[i].getFConst();
                 float length = VectorLength(operandArray, objectSize);
                 if (length)
                     resultArray[i].setFConst(x / length);
                 else
-                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
-                                                  diagnostics, &resultArray[i]);
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink,
+                                                  &resultArray[i]);
                 break;
             }
+            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
+            return nullptr;
 
-            case EOpDFdx:
-            case EOpDFdy:
-            case EOpFwidth:
-                ASSERT(getType().getBasicType() == EbtFloat);
+          case EOpDFdx:
+          case EOpDFdy:
+          case EOpFwidth:
+            if (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;
 }
 
-void TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion &parameter,
-                                              FloatTypeUnaryFunc builtinFunc,
-                                              TConstantUnion *result) const
+bool TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion &parameter, FloatTypeUnaryFunc builtinFunc,
+                                              TInfoSink &infoSink, TConstantUnion *result) const
 {
     ASSERT(builtinFunc);
 
-    ASSERT(getType().getBasicType() == EbtFloat);
-    result->setFConst(builtinFunc(parameter.getFConst()));
+    if (getType().getBasicType() == EbtFloat)
+    {
+        result->setFConst(builtinFunc(parameter.getFConst()));
+        return true;
+    }
+
+    infoSink.info.message(
+        EPrefixInternalError, getLine(),
+        "Unary operation not folded into constant");
+    return false;
 }
 
 // static
-TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate)
+TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate,
+                                                               TInfoSink &infoSink)
 {
     ASSERT(aggregate->getSequence()->size() > 0u);
     size_t resultSize           = aggregate->getType().getObjectSize();
     TConstantUnion *resultArray = new TConstantUnion[resultSize];
     TBasicType basicType        = aggregate->getBasicType();
 
     size_t resultIndex = 0u;
 
@@ -2016,18 +1914,17 @@ TConstantUnion *TIntermConstantUnion::Fo
             ++resultIndex;
         }
     }
     ASSERT(resultIndex == resultSize);
     return resultArray;
 }
 
 // static
-TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate,
-                                                           TDiagnostics *diagnostics)
+TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink)
 {
     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;
@@ -2058,560 +1955,560 @@ TConstantUnion *TIntermConstantUnion::Fo
     TConstantUnion *resultArray = nullptr;
     if (paramsCount == 2)
     {
         //
         // Binary built-in
         //
         switch (op)
         {
-            case EOpAtan:
+          case EOpAtan:
             {
-                ASSERT(basicType == EbtFloat);
-                resultArray = new TConstantUnion[maxObjectSize];
-                for (size_t i = 0; i < maxObjectSize; i++)
+                if (basicType == EbtFloat)
                 {
-                    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));
+                    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));
+                    }
                 }
-                break;
+                else
+                    UNREACHABLE();
             }
+            break;
 
-            case EOpPow:
+          case EOpPow:
             {
-                ASSERT(basicType == EbtFloat);
-                resultArray = new TConstantUnion[maxObjectSize];
-                for (size_t i = 0; i < maxObjectSize; i++)
+                if (basicType == EbtFloat)
                 {
-                    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));
+                    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));
+                    }
                 }
-                break;
+                else
+                    UNREACHABLE();
             }
+            break;
 
-            case EOpMod:
+          case EOpMod:
             {
-                ASSERT(basicType == EbtFloat);
-                resultArray = new TConstantUnion[maxObjectSize];
-                for (size_t i = 0; i < maxObjectSize; i++)
+                if (basicType == EbtFloat)
                 {
-                    float x = unionArrays[0][i].getFConst();
-                    float y = unionArrays[1][i].getFConst();
-                    resultArray[i].setFConst(x - y * floorf(x / y));
+                    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));
+                    }
                 }
-                break;
+                else
+                    UNREACHABLE();
             }
+            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:
             {
-                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;
+                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();
             }
+            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;
             }
-            case EOpGreaterThanEqual:
+            break;
+
+          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:
+          case EOpDistance:
+            if (basicType == EbtFloat)
             {
-                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));
-                break;
             }
+            else
+                UNREACHABLE();
+            break;
 
-            case EOpDot:
-                ASSERT(basicType == EbtFloat);
-                resultArray = new TConstantUnion();
-                resultArray->setFConst(
-                    VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize));
-                break;
+          case EOpDot:
 
-            case EOpCross:
+            if (basicType == EbtFloat)
             {
-                ASSERT(basicType == EbtFloat && maxObjectSize == 3);
+                resultArray = new TConstantUnion();
+                resultArray->setFConst(VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize));
+            }
+            else
+                UNREACHABLE();
+            break;
+
+          case EOpCross:
+            if (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:
+          case EOpReflect:
+            if (basicType == EbtFloat)
             {
-                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:
+          case EOpMul:
+            if (basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() &&
+                (*sequence)[1]->getAsTyped()->isMatrix())
             {
-                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:
+          case EOpOuterProduct:
+            if (basicType == EbtFloat)
             {
-                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, diagnostics,
-                                                              &resultArray[i]);
+                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
                             else
                                 resultArray[i].setFConst(gl::clamp(x, min, max));
-                            break;
                         }
-
-                        case EbtInt:
+                        break;
+                      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, diagnostics,
-                                                              &resultArray[i]);
+                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
                             else
                                 resultArray[i].setIConst(gl::clamp(x, min, max));
-                            break;
                         }
-                        case EbtUInt:
+                        break;
+                      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, diagnostics,
-                                                              &resultArray[i]);
+                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
                             else
                                 resultArray[i].setUConst(gl::clamp(x, min, max));
-                            break;
                         }
-                        default:
-                            UNREACHABLE();
-                            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);
+                        }
                     }
                 }
-                break;
+                else
+                    UNREACHABLE();
             }
+            break;
 
-            case EOpMix:
+          case EOpSmoothStep:
             {
-                ASSERT(basicType == EbtFloat);
-                resultArray = new TConstantUnion[maxObjectSize];
-                for (size_t i = 0; i < maxObjectSize; i++)
+                if (basicType == EbtFloat)
                 {
-                    float x         = unionArrays[0][i].getFConst();
-                    float y         = unionArrays[1][i].getFConst();
-                    TBasicType type = (*sequence)[2]->getAsTyped()->getType().getBasicType();
-                    if (type == EbtFloat)
+                    resultArray = new TConstantUnion[maxObjectSize];
+                    for (size_t i = 0; i < maxObjectSize; i++)
                     {
-                        // 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);
+                        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));
+                        }
                     }
                 }
-                break;
+                else
+                    UNREACHABLE();
             }
+            break;
 
-            case EOpSmoothStep:
+          case EOpFaceForward:
+            if (basicType == EbtFloat)
             {
-                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:
+          case EOpRefract:
+            if (basicType == EbtFloat)
             {
-                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
+                UNREACHABLE();
+            break;
 
-            default:
-                UNREACHABLE();
-                // TODO: Add constant folding support for other built-in operations that take 3
-                // parameters and not handled above.
-                return nullptr;
+          default:
+            UNREACHABLE();
+            // 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,17 +29,16 @@
 
 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;
@@ -89,17 +88,16 @@ 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; }
 
@@ -156,19 +154,16 @@ 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.
@@ -316,17 +311,16 @@ 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; }
 
@@ -344,56 +338,53 @@ 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);
-    const TConstantUnion *foldIndexing(int index);
-    TConstantUnion *foldUnaryNonComponentWise(TOperator op);
-    TConstantUnion *foldUnaryComponentWise(TOperator op, TDiagnostics *diagnostics);
+    TConstantUnion *foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink);
+    TConstantUnion *foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink);
 
-    static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate);
-    static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate,
-                                                TDiagnostics *diagnostics);
+    static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate,
+                                                    TInfoSink &infoSink);
+    static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink);
 
   protected:
     // Same data may be shared between multiple constant unions, so it can't be modified.
     const TConstantUnion *mUnionArrayPointer;
 
   private:
     typedef float(*FloatTypeUnaryFunc) (float);
-    void foldFloatTypeUnary(const TConstantUnion &parameter,
-                            FloatTypeUnaryFunc builtinFunc,
-                            TConstantUnion *result) const;
+    bool foldFloatTypeUnary(const TConstantUnion &parameter, FloatTypeUnaryFunc builtinFunc, TInfoSink &infoSink, 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:
@@ -410,33 +401,40 @@ 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:
@@ -453,42 +451,49 @@ class TIntermBinary : public TIntermOper
 };
 
 //
 // Nodes for unary math operators.
 //
 class TIntermUnary : public TIntermOperator
 {
   public:
-    TIntermUnary(TOperator op, TIntermTyped *operand);
+    TIntermUnary(TOperator op, const TType &type)
+        : TIntermOperator(op, type),
+          mOperand(NULL),
+          mUseEmulatedFunction(false) {}
+    TIntermUnary(TOperator op)
+        : TIntermOperator(op),
+          mOperand(NULL),
+          mUseEmulatedFunction(false) {}
 
     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; }
-    TIntermTyped *fold(TDiagnostics *diagnostics);
+    void promote(const TType *funcReturnType);
+    TIntermTyped *fold(TInfoSink &infoSink);
 
     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.
@@ -512,29 +517,26 @@ 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(TDiagnostics *diagnostics);
+    TIntermTyped *fold(TInfoSink &infoSink);
 
     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; }
@@ -564,71 +566,56 @@ class TIntermAggregate : public TIntermO
     bool mUseEmulatedFunction;
 
     bool mGotPrecisionFromChildren;
 
   private:
     TIntermAggregate(const TIntermAggregate &node);  // note: not deleted, just private!
 };
 
-// For ternary operators like a ? b : c.
-class TIntermTernary : public TIntermTyped
+//
+// For if tests.
+//
+class TIntermSelection : public TIntermTyped
 {
   public:
-    TIntermTernary(TIntermTyped *cond, TIntermTyped *trueExpression, TIntermTyped *falseExpression);
+    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); }
 
     void traverse(TIntermTraverser *it) override;
     bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
 
-    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);
+    // Conservatively assume selections have side-effects
+    bool hasSideEffects() const override { return true; }
 
-  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; }
+    bool usesTernaryOperator() const { return getBasicType() != EbtVoid; }
+    TIntermNode *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:
@@ -704,33 +691,31 @@ 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; }
@@ -1008,17 +993,16 @@ 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,16 +100,19 @@ bool IntermNodePatternMatcher::match(TIn
             {
                 return true;
             }
         }
     }
     return false;
 }
 
-bool IntermNodePatternMatcher::match(TIntermTernary *node)
+bool IntermNodePatternMatcher::match(TIntermSelection *node)
 {
     if ((mMask & kUnfoldedShortCircuitExpression) != 0)
     {
-        return true;
+        if (node->usesTernaryOperator())
+        {
+            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 TIntermTernary;
+class TIntermSelection;
 
 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(TIntermTernary *node);
+    bool match(TIntermSelection *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,21 +28,16 @@ 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);
@@ -147,31 +142,37 @@ 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, tempSymbol, initializer);
+    TIntermBinary *tempInit = new TIntermBinary(EOpInitialize);
+    tempInit->setLeft(tempSymbol);
+    tempInit->setRight(initializer);
+    tempInit->setType(tempSymbol->getType());
     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, tempSymbol, rightNode);
+    TIntermBinary *assignment = new TIntermBinary(EOpAssign);
+    assignment->setLeft(tempSymbol);
+    assignment->setRight(rightNode);
+    assignment->setType(tempSymbol->getType());
     return assignment;
 }
 
 void TIntermTraverser::useTemporaryIndex(unsigned int *temporaryIndex)
 {
     mTemporaryIndex = temporaryIndex;
 }
 
@@ -567,41 +568,16 @@ 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,30 +39,48 @@ 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,
-                                      TDiagnostics *diagnostics)
+TIntermTyped *TIntermediate::addIndex(
+    TOperator op, TIntermTyped *base, TIntermTyped *index, const TSourceLoc &line)
 {
-    TIntermBinary *node = new TIntermBinary(op, base, index);
+    TIntermBinary *node = new TIntermBinary(op);
     node->setLine(line);
+    node->setLeft(base);
+    node->setRight(index);
+
+    // caller should set the type
+
+    return node;
+}
 
-    TIntermTyped *folded = node->fold(diagnostics);
-    if (folded)
-    {
-        return folded;
-    }
+//
+// 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;
 
     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
@@ -231,47 +249,53 @@ 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 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)
+// 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)
 {
+    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))
         {
-            trueExpression->getTypePointer()->setQualifier(resultQualifier);
-            return trueExpression;
+            trueBlock->getTypePointer()->setQualifier(resultQualifier);
+            return trueBlock;
         }
         else
         {
-            falseExpression->getTypePointer()->setQualifier(resultQualifier);
-            return falseExpression;
+            falseBlock->getTypePointer()->setQualifier(resultQualifier);
+            return falseBlock;
         }
     }
 
-    // Make a ternary node.
-    TIntermTernary *node = new TIntermTernary(cond, trueExpression, falseExpression);
+    //
+    // Make a selection node.
+    //
+    TIntermSelection *node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType());
+    node->getTypePointer()->setQualifier(resultQualifier);
     node->setLine(line);
 
     return node;
 }
 
 TIntermSwitch *TIntermediate::addSwitch(
     TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &line)
 {
@@ -306,17 +330,16 @@ 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++)
     {
@@ -360,17 +383,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();
@@ -383,18 +406,17 @@ TIntermAggregate *TIntermediate::PostPro
         aggRoot = new TIntermAggregate(EOpSequence);
         aggRoot->setLine(root->getLine());
         aggRoot->getSequence()->push_back(root);
     }
 
     return aggRoot;
 }
 
-TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate,
-                                                  TDiagnostics *diagnostics)
+TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate)
 {
     switch (aggregate->getOp())
     {
         case EOpAtan:
         case EOpPow:
         case EOpMod:
         case EOpMin:
         case EOpMax:
@@ -411,19 +433,19 @@ TIntermTyped *TIntermediate::foldAggrega
         case EOpVectorEqual:
         case EOpVectorNotEqual:
         case EOpDistance:
         case EOpDot:
         case EOpCross:
         case EOpFaceForward:
         case EOpReflect:
         case EOpRefract:
-            return aggregate->fold(diagnostics);
+            return aggregate->fold(mInfoSink);
         default:
             // TODO: Add support for folding array constructors
             if (aggregate->isConstructor() && !aggregate->isArray())
             {
-                return aggregate->fold(diagnostics);
+                return aggregate->fold(mInfoSink);
             }
             // 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,62 +11,61 @@
 
 struct TVectorFields
 {
     int offsets[4];
     int num;
 };
 
 //
-// Set of helper functions to help build the tree.
+// Set of helper functions to help parse and build the tree.
 //
+class TInfoSink;
 class TIntermediate
 {
   public:
     POOL_ALLOCATOR_NEW_DELETE();
-    TIntermediate() {}
+    TIntermediate(TInfoSink &i)
+        : mInfoSink(i) { }
 
     TIntermSymbol *addSymbol(
         int id, const TString &, const TType &, const TSourceLoc &);
-    TIntermTyped *addIndex(TOperator op,
-                           TIntermTyped *base,
-                           TIntermTyped *index,
-                           const TSourceLoc &line,
-                           TDiagnostics *diagnostics);
+    TIntermTyped *addIndex(
+        TOperator op, TIntermTyped *base, TIntermTyped *index, const TSourceLoc &);
     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 &line);
-    static TIntermTyped *AddTernarySelection(TIntermTyped *cond,
-                                             TIntermTyped *trueExpression,
-                                             TIntermTyped *falseExpression,
-                                             const TSourceLoc &line);
+    TIntermNode *addSelection(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &);
+    TIntermTyped *addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock,
+                               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 &);
-    static TIntermAggregate *PostProcess(TIntermNode *root);
+    TIntermAggregate *postProcess(TIntermNode *root);
 
     static void outputTree(TIntermNode *, TInfoSinkBase &);
 
-    TIntermTyped *foldAggregateBuiltIn(TIntermAggregate *aggregate, TDiagnostics *diagnostics);
+    TIntermTyped *foldAggregateBuiltIn(TIntermAggregate *aggregate);
 
   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,19 +22,21 @@ TString arrayBrackets(const TType &type)
 
 bool isSingleStatement(TIntermNode *node)
 {
     if (const TIntermAggregate *aggregate = node->getAsAggregate())
     {
         return (aggregate->getOp() != EOpFunction) &&
                (aggregate->getOp() != EOpSequence);
     }
-    else if (node->getAsSelectionNode())
+    else if (const TIntermSelection *selection = node->getAsSelectionNode())
     {
-        return false;
+        // Ternary operators are usually part of an assignment operator.
+        // This handles those rare cases in which they are all by themselves.
+        return selection->usesTernaryOperator();
     }
     else if (node->getAsLoopNode())
     {
         return false;
     }
     else if (node->getAsSwitchNode())
     {
         return false;
@@ -704,50 +706,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();
 
-    out << "if (";
-    node->getCondition()->traverse(this);
-    out << ")\n";
-
-    incrementDepth(node);
-    visitCodeBlock(node->getTrueBlock());
+    if (node->usesTernaryOperator())
+    {
+        // 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 << "))";
+    }
+    else
+    {
+        out << "if (";
+        node->getCondition()->traverse(this);
+        out << ")\n";
 
-    if (node->getFalseBlock())
-    {
-        out << "else\n";
-        visitCodeBlock(node->getFalseBlock());
+        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,17 +39,16 @@ 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,24 +75,21 @@ 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,
-                       ShCompileOptions compileOptions)
+OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion,
+    const TExtensionBehavior &extensionBehavior,
+    const char *sourcePath, ShShaderOutput outputType,
+    int numRenderTargets, const std::vector<Uniform> &uniforms,
+    int compileOptions)
     : TIntermTraverser(true, true, true),
       mShaderType(shaderType),
       mShaderVersion(shaderVersion),
       mExtensionBehavior(extensionBehavior),
       mSourcePath(sourcePath),
       mOutputType(outputType),
       mCompileOptions(compileOptions),
       mNumRenderTargets(numRenderTargets),
@@ -1454,18 +1451,19 @@ 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.
-                if ((*sit)->getAsCaseNode() == nullptr && (*sit)->getAsSelectionNode() == nullptr &&
-                    !IsSequence(*sit))
+                TIntermSelection *asSelection = (*sit)->getAsSelectionNode();
+                ASSERT(asSelection == nullptr || !asSelection->usesTernaryOperator());
+                if ((*sit)->getAsCaseNode() == nullptr && asSelection == nullptr && !IsSequence(*sit))
                     out << ";\n";
             }
 
             if (mInsideFunction)
             {
                 outputLineDirective(out, node->getLine().last_line);
                 out << "}\n";
             }
@@ -1979,28 +1977,21 @@ 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,24 +25,21 @@ 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,
-               ShCompileOptions compileOptions);
+    OutputHLSL(sh::GLenum shaderType, int shaderVersion,
+        const TExtensionBehavior &extensionBehavior,
+        const char *sourcePath, ShShaderOutput outputType,
+        int numRenderTargets, const std::vector<Uniform> &uniforms,
+        int 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;
 
@@ -56,17 +53,16 @@ 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);
@@ -116,17 +112,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;
-    ShCompileOptions mCompileOptions;
+    int 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,22 +208,16 @@ 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:
@@ -448,16 +442,21 @@ 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;
@@ -657,24 +656,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.getBasicType() != EbtBool || pType.isAggregate())
+    if (pType.type != EbtBool || pType.isAggregate())
     {
         error(line, "boolean expression expected", "");
     }
 }
 
 bool TParseContext::checkIsNotSampler(const TSourceLoc &line,
-                                      const TTypeSpecifierNonArray &pType,
+                                      const TPublicType &pType,
                                       const char *reason)
 {
     if (pType.type == EbtStruct)
     {
         if (containsSampler(*pType.userDef))
         {
             error(line, reason, getBasicString(pType.type), "(structure contains a sampler)");
             return false;
@@ -819,17 +818,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.getBasicType() == EbtStruct &&
+    if (mShaderVersion >= 300 && elementType.type == EbtStruct &&
         sh::IsVarying(elementType.qualifier))
     {
         error(line, "cannot declare arrays of structs of this qualifier",
               TType(elementType).getCompleteString().c_str());
         return false;
     }
 
     return true;
@@ -924,34 +923,37 @@ bool TParseContext::declareVariable(cons
     }
 
     if (!checkIsNonVoid(line, identifier, type.getBasicType()))
         return false;
 
     return true;
 }
 
-void TParseContext::checkIsParameterQualifierValid(
-    const TSourceLoc &line,
-    const TTypeQualifierBuilder &typeQualifierBuilder,
-    TType *type)
+void TParseContext::checkIsParameterQualifierValid(const TSourceLoc &line,
+                                                   TQualifier qualifier,
+                                                   TQualifier paramQualifier,
+                                                   TType *type)
 {
-    TTypeQualifier typeQualifier = typeQualifierBuilder.getParameterTypeQualifier(&mDiagnostics);
-
-    if (typeQualifier.qualifier == EvqOut || typeQualifier.qualifier == EvqInOut)
+    if (qualifier != EvqConst && qualifier != EvqTemporary)
     {
-        checkOutParameterIsNotSampler(line, typeQualifier.qualifier, *type);
+        error(line, "qualifier not allowed on function parameter", getQualifierString(qualifier));
+        return;
     }
-
-    type->setQualifier(typeQualifier.qualifier);
-
-    if (typeQualifier.precision != EbpUndefined)
+    if (qualifier == EvqConst && paramQualifier != EvqIn)
     {
-        type->setPrecision(typeQualifier.precision);
+        error(line, "qualifier not allowed with ", getQualifierString(qualifier),
+              getQualifierString(paramQualifier));
+        return;
     }
+
+    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())
     {
@@ -981,30 +983,29 @@ void TParseContext::singleDeclarationErr
     switch (publicType.qualifier)
     {
         case EvqVaryingIn:
         case EvqVaryingOut:
         case EvqAttribute:
         case EvqVertexIn:
         case EvqFragmentOut:
         case EvqComputeIn:
-            if (publicType.getBasicType() == EbtStruct)
+            if (publicType.type == EbtStruct)
             {
                 error(identifierLocation, "cannot be used with a structure",
                       getQualifierString(publicType.qualifier));
                 return;
             }
 
         default:
             break;
     }
 
     if (publicType.qualifier != EvqUniform &&
-        !checkIsNotSampler(identifierLocation, publicType.typeSpecifierNonArray,
-                           "samplers must be uniform"))
+        !checkIsNotSampler(identifierLocation, publicType, "samplers must be uniform"))
     {
         return;
     }
 
     // check for layout qualifier issues
     const TLayoutQualifier layoutQualifier = publicType.layoutQualifier;
 
     if (layoutQualifier.matrixPacking != EmpUnspecified)
@@ -1071,37 +1072,22 @@ void TParseContext::functionCallLValueEr
                 error(argument->getLine(),
                       "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error");
                 return;
             }
         }
     }
 }
 
-void TParseContext::checkInvariantVariableQualifier(bool invariant,
-                                                    const TQualifier qualifier,
-                                                    const TSourceLoc &invariantLocation)
+void TParseContext::checkInvariantIsOutVariableES3(const TQualifier qualifier,
+                                                   const TSourceLoc &invariantLocation)
 {
-    if (!invariant)
-        return;
-
-    if (mShaderVersion < 300)
+    if (!sh::IsVaryingOut(qualifier) && qualifier != EvqFragmentOut)
     {
-        // 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");
-        }
+        error(invariantLocation, "Only out variables can be invariant.", "invariant");
     }
 }
 
 bool TParseContext::supportsExtension(const char *extension)
 {
     const TExtensionBehavior &extbehavior   = extensionBehavior();
     TExtensionBehavior::const_iterator iter = extbehavior.find(extension);
     return (iter != extbehavior.end());
@@ -1394,90 +1380,74 @@ bool TParseContext::executeInitializer(c
     {
         assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
         return true;
     }
 
     return false;
 }
 
-TPublicType TParseContext::addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
+TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier,
+                                                 bool invariant,
+                                                 TLayoutQualifier layoutQualifier,
                                                  const TPublicType &typeSpecifier)
 {
-    TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
-
     TPublicType returnType     = typeSpecifier;
-    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);
+    returnType.qualifier       = qualifier;
+    returnType.invariant       = invariant;
+    returnType.layoutQualifier = layoutQualifier;
+
+    checkWorkGroupSizeIsNotSpecified(typeSpecifier.line, layoutQualifier);
 
     if (mShaderVersion < 300)
     {
         if (typeSpecifier.array)
         {
-            error(typeSpecifier.getLine(), "not supported", "first-class array");
+            error(typeSpecifier.line, "not supported", "first-class array");
             returnType.clearArrayness();
         }
 
-        if (returnType.qualifier == EvqAttribute &&
-            (typeSpecifier.getBasicType() == EbtBool || typeSpecifier.getBasicType() == EbtInt))
+        if (qualifier == EvqAttribute &&
+            (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt))
         {
-            error(typeSpecifier.getLine(), "cannot be bool or int",
-                  getQualifierString(returnType.qualifier));
+            error(typeSpecifier.line, "cannot be bool or int", getQualifierString(qualifier));
         }
 
-        if ((returnType.qualifier == EvqVaryingIn || returnType.qualifier == EvqVaryingOut) &&
-            (typeSpecifier.getBasicType() == EbtBool || typeSpecifier.getBasicType() == EbtInt))
+        if ((qualifier == EvqVaryingIn || qualifier == EvqVaryingOut) &&
+            (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt))
         {
-            error(typeSpecifier.getLine(), "cannot be bool or int",
-                  getQualifierString(returnType.qualifier));
+            error(typeSpecifier.line, "cannot be bool or int", getQualifierString(qualifier));
         }
     }
     else
     {
-        if (!returnType.layoutQualifier.isEmpty())
+        if (!layoutQualifier.isEmpty())
         {
-            checkIsAtGlobalLevel(typeSpecifier.getLine(), "layout");
+            checkIsAtGlobalLevel(typeSpecifier.line, "layout");
         }
-        if (sh::IsVarying(returnType.qualifier) || returnType.qualifier == EvqVertexIn ||
-            returnType.qualifier == EvqFragmentOut)
+        if (sh::IsVarying(qualifier) || qualifier == EvqVertexIn || qualifier == EvqFragmentOut)
         {
-            checkInputOutputTypeIsValidES3(returnType.qualifier, typeSpecifier,
-                                           typeSpecifier.getLine());
+            checkInputOutputTypeIsValidES3(qualifier, typeSpecifier, typeSpecifier.line);
         }
-        if (returnType.qualifier == EvqComputeIn)
+        if (qualifier == EvqComputeIn)
         {
-            error(typeSpecifier.getLine(), "'in' can be only used to specify the local group size",
+            error(typeSpecifier.line, "'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.getBasicType() == EbtBool)
+    if (type.type == EbtBool)
     {
         error(qualifierLocation, "cannot be bool", getQualifierString(qualifier));
     }
 
     // Specific restrictions apply for vertex shader inputs and fragment shader outputs.
     switch (qualifier)
     {
         case EvqVertexIn:
@@ -1485,38 +1455,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.typeSpecifierNonArray.isMatrix())
+            if (type.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.getBasicType() == EbtInt || type.getBasicType() == EbtUInt ||
-         type.isStructureContainingType(EbtInt) || type.isStructureContainingType(EbtUInt));
+        (type.type == EbtInt || type.type == EbtUInt || type.isStructureContainingType(EbtInt) ||
+         type.isStructureContainingType(EbtUInt));
     if (typeContainsIntegers && qualifier != EvqFlatIn && qualifier != EvqFlatOut)
     {
         error(qualifierLocation, "must use 'flat' interpolation here",
               getQualifierString(qualifier));
     }
 
-    if (type.getBasicType() == EbtStruct)
+    if (type.type == 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));
@@ -1693,68 +1663,50 @@ TIntermAggregate *TParseContext::parseSi
         return initNode ? intermediate.makeAggregate(initNode, initLocation) : nullptr;
     }
     else
     {
         return nullptr;
     }
 }
 
-TIntermAggregate *TParseContext::parseInvariantDeclaration(
-    const TTypeQualifierBuilder &typeQualifierBuilder,
-    const TSourceLoc &identifierLoc,
-    const TString *identifier,
-    const TSymbol *symbol)
+TIntermAggregate *TParseContext::parseInvariantDeclaration(const TSourceLoc &invariantLoc,
+                                                           const TSourceLoc &identifierLoc,
+                                                           const TString *identifier,
+                                                           const TSymbol *symbol)
 {
-    TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
-
-    if (!typeQualifier.invariant)
-    {
-        error(identifierLoc, "Expected invariant", identifier->c_str());
+    // invariant declaration
+    if (!checkIsAtGlobalLevel(invariantLoc, "invariant varying"))
         return nullptr;
-    }
-    if (!checkIsAtGlobalLevel(identifierLoc, "invariant varying"))
-    {
-        return nullptr;
-    }
+
     if (!symbol)
     {
         error(identifierLoc, "undeclared identifier declared as invariant", identifier->c_str());
         return nullptr;
     }
-    if (!IsQualifierUnspecified(typeQualifier.qualifier))
-    {
-        error(identifierLoc, "invariant declaration specifies qualifier",
-              getQualifierString(typeQualifier.qualifier));
-    }
-    if (typeQualifier.precision != EbpUndefined)
+    else
     {
-        error(identifierLoc, "invariant declaration specifies precision",
-              getPrecisionString(typeQualifier.precision));
-    }
-    if (!typeQualifier.layoutQualifier.isEmpty())
-    {
-        error(identifierLoc, "invariant declaration specifies layout", "'layout'");
+        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;
     }
-
-    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
@@ -1905,24 +1857,20 @@ TIntermAggregate *TParseContext::parseAr
         }
     }
     else
     {
         return nullptr;
     }
 }
 
-void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder)
+void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier)
 {
-    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())
@@ -2268,18 +2216,17 @@ 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.typeSpecifierNonArray,
-                      "samplers can't be function return values");
+    checkIsNotSampler(location, type, "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())
         {
@@ -2291,35 +2238,34 @@ 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.getLine(), "constructor can't be a structure definition",
-              getBasicString(publicType.getBasicType()));
+        error(publicType.line, "constructor can't be a structure definition",
+              getBasicString(publicType.type));
     }
 
     TOperator op = EOpNull;
-    if (publicType.getUserDef())
+    if (publicType.userDef)
     {
         op = EOpConstructStruct;
     }
     else
     {
         op = sh::TypeToConstructorOperator(TType(publicType));
         if (op == EOpNull)
         {
-            error(publicType.getLine(), "cannot construct this type",
-                  getBasicString(publicType.getBasicType()));
-            publicType.setBasicType(EbtFloat);
+            error(publicType.line, "cannot construct this type", getBasicString(publicType.type));
+            publicType.type = EbtFloat;
             op              = EOpConstructFloat;
         }
     }
 
     TString tempString;
     const TType *type = new TType(publicType);
     return new TFunction(&tempString, type, op);
 }
@@ -2371,53 +2317,137 @@ TIntermTyped *TParseContext::addConstruc
     if (op != EOpConstructStruct)
     {
         constructor->setPrecisionFromChildren();
         type.setPrecision(constructor->getPrecision());
     }
 
     constructor->setType(type);
 
-    TIntermTyped *constConstructor = intermediate.foldAggregateBuiltIn(constructor, &mDiagnostics);
+    TIntermTyped *constConstructor = intermediate.foldAggregateBuiltIn(constructor);
     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 TTypeQualifierBuilder &typeQualifierBuilder,
-    const TSourceLoc &nameLine,
-    const TString &blockName,
-    TFieldList *fieldList,
-    const TString *instanceName,
-    const TSourceLoc &instanceLine,
-    TIntermTyped *arrayIndex,
-    const TSourceLoc &arrayIndexLine)
+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)
 {
     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;
     }
 
@@ -2452,21 +2482,16 @@ 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");
@@ -2600,32 +2625,29 @@ 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.
@@ -2645,88 +2667,161 @@ 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 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.
+        // 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.
         bool outOfRangeIndexIsError = indexExpression->getQualifier() == EvqConst;
         int index = indexConstantUnion->getIConst(0);
-
-        int safeIndex = -1;
-
-        if (baseExpression->isArray())
+        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())
+            {
+                index = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
+                                             baseExpression->getType().getNominalSize(),
+                                             "vector field selection out of range", "[]");
+            }
+            else if (baseExpression->isMatrix())
+            {
+                index = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
+                                             baseExpression->getType().getCols(),
+                                             "matrix field selection out of range", "[]");
+            }
+        }
+
+        TIntermConstantUnion *baseConstantUnion = baseExpression->getAsConstantUnion();
+        if (baseConstantUnion)
         {
-            if (baseExpression->getQualifier() == EvqFragData && index > 0)
+            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())
             {
-                if (mShaderSpec == SH_WEBGL2_SPEC)
+                // 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);
+            }
+        }
+        else
+        {
+            int safeIndex = -1;
+
+            if (baseExpression->isArray())
+            {
+                if (baseExpression->getQualifier() == EvqFragData && index > 0)
                 {
-                    // Error has been already generated if index is not const.
-                    if (indexExpression->getQualifier() == EvqConst)
+                    if (mShaderSpec == SH_WEBGL2_SPEC)
                     {
-                        error(location, "", "[",
-                              "array index for gl_FragData must be constant zero");
+                        // 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;
                     }
-                    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 (!isExtensionEnabled("GL_EXT_draw_buffers"))
+                // Only do generic out-of-range check if similar error hasn't already been reported.
+                if (safeIndex < 0)
                 {
-                    outOfRangeError(outOfRangeIndexIsError, location, "", "[",
-                                    "array index for gl_FragData must be zero when "
-                                    "GL_EXT_draw_buffers is disabled");
-                    safeIndex = 0;
+                    safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
+                                                     baseExpression->getArraySize(),
+                                                     "array index out of range", "[]");
                 }
             }
-            // Only do generic out-of-range check if similar error hasn't already been reported.
-            if (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 != -1)
             {
-                safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
-                                                 baseExpression->getArraySize(),
-                                                 "array index out of range", "[]");
+                TConstantUnion *safeConstantUnion = new TConstantUnion();
+                safeConstantUnion->setIConst(safeIndex);
+                indexConstantUnion->replaceConstantUnion(safeConstantUnion);
             }
-        }
-        else if (baseExpression->isMatrix())
-        {
-            safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
-                                             baseExpression->getType().getCols(),
-                                             "matrix field selection out of range", "[]");
+
+            indexedExpression =
+                intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location);
         }
-        else if (baseExpression->isVector())
-        {
-            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
     {
-        return intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location,
-                                     &mDiagnostics);
+        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);
     }
+    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)
@@ -2749,101 +2844,143 @@ 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;
         }
 
-        TIntermTyped *index = intermediate.addSwizzle(fields, fieldLocation);
-        return intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation,
-                                     &mDiagnostics);
+        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)));
+        }
     }
     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");
-            return baseExpression;
+            indexedExpression = 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)
             {
-                TIntermTyped *index = TIntermTyped::CreateIndexNode(i);
-                index->setLine(fieldLocation);
-                return intermediate.addIndex(EOpIndexDirectStruct, baseExpression, index,
-                                             dotLocation, &mDiagnostics);
+                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());
+                }
             }
             else
             {
                 error(dotLocation, " no such field in structure", fieldString.c_str());
-                return baseExpression;
+                indexedExpression = 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");
-            return baseExpression;
+            indexedExpression = 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)
             {
-                TIntermTyped *index = TIntermTyped::CreateIndexNode(i);
-                index->setLine(fieldLocation);
-                return intermediate.addIndex(EOpIndexDirectInterfaceBlock, baseExpression, index,
-                                             dotLocation, &mDiagnostics);
+                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());
             }
             else
             {
                 error(dotLocation, " no such field in interface block", fieldString.c_str());
-                return baseExpression;
+                indexedExpression = baseExpression;
             }
         }
     }
     else
     {
         if (mShaderVersion < 300)
         {
             error(dotLocation, " field selection requires structure or vector on left hand side",
@@ -2851,18 +2988,29 @@ TIntermTyped *TParseContext::addFieldSel
         }
         else
         {
             error(dotLocation,
                   " field selection requires structure, vector, or interface block on left hand "
                   "side",
                   fieldString.c_str());
         }
-        return baseExpression;
+        indexedExpression = baseExpression;
+    }
+
+    if (baseExpression->getQualifier() == EvqConst)
+    {
+        indexedExpression->getTypePointer()->setQualifier(EvqConst);
     }
+    else
+    {
+        indexedExpression->getTypePointer()->setQualifier(EvqTemporary);
+    }
+
+    return indexedExpression;
 }
 
 TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
                                                      const TSourceLoc &qualifierTypeLine)
 {
     TLayoutQualifier qualifier = TLayoutQualifier::create();
 
     if (qualifierType == "shared")
@@ -2930,17 +3078,16 @@ 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")
@@ -2956,100 +3103,159 @@ 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)
 {
-    return sh::JoinLayoutQualifiers(leftQualifier, rightQualifier, rightQualifierLocation,
-                                    &mDiagnostics);
+    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;
 }
 
-TFieldList *TParseContext::addStructDeclaratorListWithQualifiers(
-    const TTypeQualifierBuilder &typeQualifierBuilder,
-    TPublicType *typeSpecifier,
-    TFieldList *fieldList)
+TPublicType TParseContext::joinInterpolationQualifiers(const TSourceLoc &interpolationLoc,
+                                                       TQualifier interpolationQualifier,
+                                                       const TSourceLoc &storageLoc,
+                                                       TQualifier storageQualifier)
 {
-    TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
-
-    typeSpecifier->qualifier       = typeQualifier.qualifier;
-    typeSpecifier->layoutQualifier = typeQualifier.layoutQualifier;
-    typeSpecifier->invariant       = typeQualifier.invariant;
-    if (typeQualifier.precision != EbpUndefined)
+    TQualifier mergedQualifier = EvqSmoothIn;
+
+    if (storageQualifier == EvqFragmentIn)
+    {
+        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();
+    }
+    else if (storageQualifier == EvqVertexOut)
     {
-        typeSpecifier->precision = typeQualifier.precision;
+        if (interpolationQualifier == EvqSmooth)
+            mergedQualifier = EvqSmoothOut;
+        else if (interpolationQualifier == EvqFlat)
+            mergedQualifier = EvqFlatOut;
+        else
+            UNREACHABLE();
     }
-    return addStructDeclaratorList(*typeSpecifier, fieldList);
+    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;
 }
 
 TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier,
                                                    TFieldList *fieldList)
 {
-    checkPrecisionSpecified(typeSpecifier.getLine(), typeSpecifier.precision,
-                            typeSpecifier.getBasicType());
-
-    checkIsNonVoid(typeSpecifier.getLine(), (*fieldList)[0]->name(), typeSpecifier.getBasicType());
-
-    checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), typeSpecifier.layoutQualifier);
+    checkIsNonVoid(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier.type);
+
+    checkWorkGroupSizeIsNotSpecified(typeSpecifier.line, 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.getBasicType());
-        type->setPrimarySize(typeSpecifier.getPrimarySize());
-        type->setSecondarySize(typeSpecifier.getSecondarySize());
+        type->setBasicType(typeSpecifier.type);
+        type->setPrimarySize(typeSpecifier.primarySize);
+        type->setSecondarySize(typeSpecifier.secondarySize);
         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.getLine(), typeSpecifier);
+            checkIsValidTypeForArray(typeSpecifier.line, typeSpecifier);
         }
         if (typeSpecifier.array)
             type->setArraySize(static_cast<unsigned int>(typeSpecifier.arraySize));
-        if (typeSpecifier.getUserDef())
+        if (typeSpecifier.userDef)
         {
-            type->setStruct(typeSpecifier.getUserDef()->getStruct());
+            type->setStruct(typeSpecifier.userDef->getStruct());
         }
 
-        checkIsBelowStructNestingLimit(typeSpecifier.getLine(), *(*fieldList)[i]);
+        checkIsBelowStructNestingLimit(typeSpecifier.line, *(*fieldList)[i]);
     }
 
     return fieldList;
 }
 
-TTypeSpecifierNonArray TParseContext::addStructure(const TSourceLoc &structLine,
-                                                   const TSourceLoc &nameLine,
-                                                   const TString *structName,
-                                                   TFieldList *fieldList)
+TPublicType 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))
         {
@@ -3067,31 +3273,25 @@ TTypeSpecifierNonArray TParseContext::ad
             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());
     }
 
-    TTypeSpecifierNonArray typeSpecifierNonArray;
-    typeSpecifierNonArray.initialize(EbtStruct, structLine);
-    typeSpecifierNonArray.userDef           = structureType;
-    typeSpecifierNonArray.isStructSpecifier = true;
+    TPublicType publicType;
+    publicType.setBasic(EbtStruct, EvqTemporary, structLine);
+    publicType.userDef = structureType;
+    publicType.isStructSpecifier = true;
     exitStructDeclaration();
 
-    return typeSpecifierNonArray;
+    return publicType;
 }
 
 TIntermSwitch *TParseContext::addSwitch(TIntermTyped *init,
                                         TIntermAggregate *statementList,
                                         const TSourceLoc &loc)
 {
     TBasicType switchType = init->getBasicType();
     if ((switchType != EbtInt && switchType != EbtUInt) || init->isMatrix() || init->isArray() ||
@@ -3206,24 +3406,17 @@ TIntermTyped *TParseContext::createUnary
             {
                 return nullptr;
             }
         // Operators for built-ins are already type checked against their prototype.
         default:
             break;
     }
 
-    TIntermUnary *node = new TIntermUnary(op, child);
-    node->setLine(loc);
-
-    TIntermTyped *foldedNode = node->fold(&mDiagnostics);
-    if (foldedNode)
-        return foldedNode;
-
-    return node;
+    return intermediate.addUnaryMath(op, child, loc, funcReturnType);
 }
 
 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());
@@ -3842,18 +4035,17 @@ 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, &mDiagnostics);
+                    TIntermTyped *foldedNode = intermediate.foldAggregateBuiltIn(aggregate);
                     if (foldedNode)
                     {
                         callNode = foldedNode;
                     }
                     else
                     {
                         callNode = aggregate;
                     }
@@ -3898,45 +4090,44 @@ TIntermTyped *TParseContext::addFunction
             callNode = intermediate.addConstantUnion(unionArray,
                                                      TType(EbtFloat, EbpUndefined, EvqConst), loc);
         }
     }
     return callNode;
 }
 
 TIntermTyped *TParseContext::addTernarySelection(TIntermTyped *cond,
-                                                 TIntermTyped *trueExpression,
-                                                 TIntermTyped *falseExpression,
+                                                 TIntermTyped *trueBlock,
+                                                 TIntermTyped *falseBlock,
                                                  const TSourceLoc &loc)
 {
     checkIsScalarBool(loc, cond);
 
-    if (trueExpression->getType() != falseExpression->getType())
+    if (trueBlock->getType() != falseBlock->getType())
     {
-        binaryOpError(loc, ":", trueExpression->getCompleteString(),
-                      falseExpression->getCompleteString());
-        return falseExpression;
+        binaryOpError(loc, ":", trueBlock->getCompleteString(), falseBlock->getCompleteString());
+        return falseBlock;
     }
     // ESSL1 sections 5.2 and 5.7:
     // ESSL3 section 5.7:
     // Ternary operator is not among the operators allowed for structures/arrays.
-    if (trueExpression->isArray() || trueExpression->getBasicType() == EbtStruct)
+    if (trueBlock->isArray() || trueBlock->getBasicType() == EbtStruct)
     {
         error(loc, "ternary operator is not allowed for structures or arrays", ":");
-        return falseExpression;
+        return falseBlock;
     }
     // 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 && trueExpression->getBasicType() == EbtVoid)
+    if (mShaderSpec == SH_WEBGL2_SPEC && trueBlock->getBasicType() == EbtVoid)
     {
         error(loc, "ternary operator is not allowed for void", ":");
-        return falseExpression;
+        return falseBlock;
     }
 
-    return TIntermediate::AddTernarySelection(cond, trueExpression, falseExpression, loc);
+    return intermediate.addSelection(cond, trueBlock, falseBlock, 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,17 +6,16 @@
 #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;
@@ -26,23 +25,24 @@ 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,
-                  ShCompileOptions options,
+                  int options,
                   bool checksPrecErrors,
                   TInfoSink &is,
                   const ShBuiltInResources &resources)
-        : intermediate(),
+        : intermediate(interm),
           symbolTable(symt),
           mDeferredSingleDeclarationErrorCheck(false),
           mShaderType(type),
           mShaderSpec(spec),
           mCompileOptions(options),
           mShaderVersion(100),
           mTreeRoot(nullptr),
           mLoopNestingLevel(0),
@@ -62,18 +62,17 @@ 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),
-          mDeclaringFunction(false)
+          mComputeShaderLocalSizeDeclared(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; }
@@ -115,22 +114,16 @@ 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);
 
@@ -154,41 +147,39 @@ 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 TTypeSpecifierNonArray &pType,
-                           const char *reason);
+    bool checkIsNotSampler(const TSourceLoc &line, const TPublicType &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 qualifier,
                                        const TType &type);
     void checkIsParameterQualifierValid(const TSourceLoc &line,
-                                        const TTypeQualifierBuilder &typeQualifierBuilder,
+                                        TQualifier qualifier,
+                                        TQualifier paramQualifier,
                                         TType *type);
     bool checkCanUseExtension(const TSourceLoc &line, const TString &extension);
     void singleDeclarationErrorCheck(const TPublicType &publicType,
                                      const TSourceLoc &identifierLocation);
     void checkLayoutQualifierSupported(const TSourceLoc &location,
                                        const TString &layoutQualifierName,
                                        int versionRequired);
     bool checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location,
                                           const TLayoutQualifier &layoutQualifier);
 
     void functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *fnCall);
-    void checkInvariantVariableQualifier(bool invariant,
-                                         const TQualifier qualifier,
-                                         const TSourceLoc &invariantLocation);
+    void checkInvariantIsOutVariableES3(const TQualifier qualifier,
+                                        const TSourceLoc &invariantLocation);
     void checkInputOutputTypeIsValidES3(const TQualifier qualifier,
                                         const TPublicType &type,
                                         const TSourceLoc &qualifierLocation);
 
     const TPragma &pragma() const { return mDirectiveHandler.pragma(); }
     const TExtensionBehavior &extensionBehavior() const { return mDirectiveHandler.extensionBehavior(); }
     bool supportsExtension(const char *extension);
     bool isExtensionEnabled(const char *extension) const;
@@ -199,17 +190,19 @@ class TParseContext : angle::NonCopyable
     const TFunction* findFunction(
         const TSourceLoc &line, TFunction *pfnCall, int inputShaderVersion, bool *builtIn = 0);
     bool executeInitializer(const TSourceLoc &line,
                             const TString &identifier,
                             const TPublicType &pType,
                             TIntermTyped *initializer,
                             TIntermNode **intermNode);
 
-    TPublicType addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
+    TPublicType addFullySpecifiedType(TQualifier qualifier,
+                                      bool invariant,
+                                      TLayoutQualifier layoutQualifier,
                                       const TPublicType &typeSpecifier);
 
     TIntermAggregate *parseSingleDeclaration(TPublicType &publicType,
                                              const TSourceLoc &identifierOrTypeLocation,
                                              const TString &identifier);
     TIntermAggregate *parseSingleArrayDeclaration(TPublicType &publicType,
                                                   const TSourceLoc &identifierLocation,
                                                   const TString &identifier,
@@ -226,17 +219,17 @@ class TParseContext : angle::NonCopyable
     TIntermAggregate *parseSingleArrayInitDeclaration(TPublicType &publicType,
                                                       const TSourceLoc &identifierLocation,
                                                       const TString &identifier,
                                                       const TSourceLoc &indexLocation,
                                                       TIntermTyped *indexExpression,
                                                       const TSourceLoc &initLocation,
                                                       TIntermTyped *initializer);
 
-    TIntermAggregate *parseInvariantDeclaration(const TTypeQualifierBuilder &typeQualifierBuilder,
+    TIntermAggregate *parseInvariantDeclaration(const TSourceLoc &invariantLoc,
                                                 const TSourceLoc &identifierLoc,
                                                 const TString *identifier,
                                                 const TSymbol *symbol);
 
     TIntermAggregate *parseDeclarator(TPublicType &publicType,
                                       TIntermAggregate *aggregateDeclaration,
                                       const TSourceLoc &identifierLocation,
                                       const TString &identifier);
@@ -258,17 +251,17 @@ class TParseContext : angle::NonCopyable
                                                TIntermAggregate *aggregateDeclaration,
                                                const TSourceLoc &identifierLocation,
                                                const TString &identifier,
                                                const TSourceLoc &indexLocation,
                                                TIntermTyped *indexExpression,
                                                const TSourceLoc &initLocation,
                                                TIntermTyped *initializer);
 
-    void parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder);
+    void parseGlobalLayoutQualifier(const TPublicType &typeQualifier);
     TIntermAggregate *addFunctionPrototypeDeclaration(const TFunction &function,
                                                       const TSourceLoc &location);
     TIntermAggregate *addFunctionDefinition(const TFunction &function,
                                             TIntermAggregate *functionPrototype,
                                             TIntermAggregate *functionBody,
                                             const TSourceLoc &location);
     void parseFunctionPrototype(const TSourceLoc &location,
                                 TFunction *function,
@@ -279,60 +272,59 @@ class TParseContext : angle::NonCopyable
                                    const TString *name,
                                    const TSourceLoc &location);
     TFunction *addConstructorFunc(const TPublicType &publicType);
     TIntermTyped *addConstructor(TIntermNode *arguments,
                                  TOperator op,
                                  TFunction *fnCall,
                                  const TSourceLoc &line);
 
+    TIntermTyped *addConstStruct(
+        const TString &identifier, TIntermTyped *node, const TSourceLoc& line);
     TIntermTyped *addIndexExpression(TIntermTyped *baseExpression,
                                      const TSourceLoc& location,
                                      TIntermTyped *indexExpression);