Translator: Clean up the compile flag passing interface

Historically, compile flags were sent to the translator as a bitmask.
Recently, we were getting close to running out of bits.  Additionally,
direct-to-metal work had started to introduce constants to be passed to
the translator, which were misplaced in ShBuiltInResources and Caps.
Recent work on Pixel Local Storage adds even more constants, aggravating
the situation.

In this change, the interface to passing compile flags is reworked.  A
struct is passed (instead of a bitmask) that has one bit for each flag.
This can be indefinitely extended.  Additionally, the constants needed
by metal and PLS are also placed in this struct.  In turn, the backends
can set these options directly, and don't have to hack them into Caps to
further get hacked into ShBuiltInResources.

Bug: angleproject:7559
Change-Id: If93f1e1b8818ad3a0ac708ab04ab93b4b397d114
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3812562
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Amirali Abdolrashidi <abdolrashidi@google.com>
Commit-Queue: Amirali Abdolrashidi <abdolrashidi@google.com>
Auto-Submit: Shahbaz Youssefi <syoussefi@chromium.org>
diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h
index 7598156..8f0c801 100644
--- a/include/GLSLANG/ShaderLang.h
+++ b/include/GLSLANG/ShaderLang.h
@@ -26,7 +26,7 @@
 
 // Version number for shader translation API.
 // It is incremented every time the API changes.
-#define ANGLE_SH_VERSION 283
+#define ANGLE_SH_VERSION 300
 
 enum ShShaderSpec
 {
@@ -87,285 +87,303 @@
     FragmentShaderInterlock_NV_GL,
     FragmentShaderOrdering_INTEL_GL,
     FragmentShaderInterlock_ARB_GL,
+
+    InvalidEnum,
+    EnumCount = InvalidEnum,
 };
 
 // Compile options.
-// The Compile options type is defined in ShaderVars.h, to allow ANGLE to import the ShaderVars
-// header without needing the ShaderLang header. This avoids some conflicts with glslang.
-// SH_VALIDATE_LOOP_INDEXING: Validates loop and indexing in the shader to
-//                            ensure that they do not exceed the minimum
-//                            functionality mandated in GLSL 1.0 spec,
-//                            Appendix A, Section 4 and 5.
-//                            There is no need to specify this parameter when
-//                            compiling for WebGL - it is implied.
-// SH_OBJECT_CODE: Translates intermediate tree to glsl or hlsl shader, or SPIR-V binary.
-//                 Can be queried by calling sh::GetObjectCode().
-// SH_VARIABLES: Extracts attributes, uniforms, and varyings.
-//               Can be queried by calling ShGetVariableInfo().
-// SH_LINE_DIRECTIVES: Emits #line directives in HLSL.
-// SH_SOURCE_PATH: Tracks the source path for shaders.
-//                 Can be queried with getSourcePath().
-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;
+struct ShCompileOptionsMetal
+{
+    // Direct-to-metal backend constants:
 
-// If requested, validates the AST after every transformation.  Useful for debugging.
-const ShCompileOptions SH_VALIDATE_AST = UINT64_C(1) << 6;
+    // Binding index for driver uniforms:
+    int driverUniformsBindingIndex;
+    // Binding index for default uniforms:
+    int defaultUniformsBindingIndex;
+    // Binding index for UBO's argument buffer
+    int UBOArgumentBufferBindingIndex;
+};
 
-// Due to spec difference between GLSL 4.1 or lower and ESSL3, some platforms (for example, Mac OSX
-// core profile) require a variable's "invariant"/"centroid" qualifiers to match between vertex and
-// fragment shader. A simple solution to allow such shaders to link is to omit the two qualifiers.
-// AMD driver in Linux requires invariant qualifier to match between vertex and fragment shaders,
-// while ESSL3 disallows invariant qualifier in fragment shader and GLSL >= 4.2 doesn't require
-// invariant qualifier to match between shaders. Remove invariant qualifier from vertex shader to
-// workaround AMD driver bug.
-// Note that the two flags take effect on ESSL3 input shaders translated to GLSL 4.1 or lower and to
-// GLSL 4.2 or newer on Linux AMD.
-// TODO(zmo): This is not a good long-term solution. Simply dropping these qualifiers may break some
-// developers' content. A more complex workaround of dynamically generating, compiling, and
-// re-linking shaders that use these qualifiers should be implemented.
-const ShCompileOptions SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3 = UINT64_C(1) << 7;
+struct ShCompileOptionsPLS
+{
+    // For ANGLE_shader_pixel_local_storage_coherent.
+    ShFragmentSynchronizationType fragmentSynchronizationType;
+};
 
-// 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;
+struct ShCompileOptions
+{
+    // Translates intermediate tree to glsl, hlsl, msl, or SPIR-V binary.  Can be queried by
+    // calling sh::GetObjectCode().
+    uint64_t objectCode : 1;
 
-// 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;
+    // Extracts attributes, uniforms, and varyings.  Can be queried by calling ShGetVariableInfo().
+    uint64_t variables : 1;
 
-// 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.
-const ShCompileOptions SH_CLAMP_INDIRECT_ARRAY_BOUNDS = UINT64_C(1) << 10;
+    // Tracks the source path for shaders.  Can be queried with getSourcePath().
+    uint64_t sourcePath : 1;
 
-// This flag limits the complexity of an expression.
-const ShCompileOptions SH_LIMIT_EXPRESSION_COMPLEXITY = UINT64_C(1) << 11;
+    // Whether the internal representation of the AST should be output.
+    uint64_t intermediateTree : 1;
 
-// This flag limits the depth of the call stack.
-const ShCompileOptions SH_LIMIT_CALL_STACK_DEPTH = UINT64_C(1) << 12;
+    // If requested, validates the AST after every transformation.  Useful for debugging.
+    uint64_t validateAST : 1;
 
-// 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;
+    // Validates loop and indexing in the shader to ensure that they do not exceed the minimum
+    // functionality mandated in GLSL 1.0 spec, Appendix A, Section 4 and 5.  There is no need to
+    // specify this parameter when compiling for WebGL - it is implied.
+    uint64_t validateLoopIndexing : 1;
 
-// 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;
+    // Emits #line directives in HLSL.
+    uint64_t lineDirectives : 1;
 
-// 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;
+    // Due to spec difference between GLSL 4.1 or lower and ESSL3, some platforms (for example, Mac
+    // OSX core profile) require a variable's "invariant"/"centroid" qualifiers to match between
+    // vertex and fragment shader. A simple solution to allow such shaders to link is to omit the
+    // two qualifiers.  AMD driver in Linux requires invariant qualifier to match between vertex and
+    // fragment shaders, while ESSL3 disallows invariant qualifier in fragment shader and GLSL >=
+    // 4.2 doesn't require invariant qualifier to match between shaders. Remove invariant qualifier
+    // from vertex shader to workaround AMD driver bug.
+    // Note that the two flags take effect on ESSL3 input shaders translated to GLSL 4.1 or lower
+    // and to GLSL 4.2 or newer on Linux AMD.
+    // TODO(zmo): This is not a good long-term solution. Simply dropping these qualifiers may break
+    // some developers' content. A more complex workaround of dynamically generating, compiling, and
+    // re-linking shaders that use these qualifiers should be implemented.
+    uint64_t removeInvariantAndCentroidForESSL3 : 1;
 
-// 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 works around bug in Intel Mac drivers related to abs(i) where i is an integer.
+    uint64_t emulateAbsIntFunction : 1;
 
-// 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;
+    // 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.
+    uint64_t enforcePackingRestrictions : 1;
 
-// 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) << 18;
+    // 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.
+    uint64_t clampIndirectArrayBounds : 1;
 
-// 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) << 19;
+    // This flag limits the complexity of an expression.
+    uint64_t limitExpressionComplexity : 1;
 
-// 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) << 20;
+    // This flag limits the depth of the call stack.
+    uint64_t limitCallStackDepth : 1;
 
-// 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) << 21;
+    // 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.
+    uint64_t initGLPosition : 1;
 
-// 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) << 22;
+    // 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.
+    uint64_t unfoldShortCircuit : 1;
 
-// 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) << 23;
+    // This flag initializes output variables to 0 at the beginning of main().  It is to avoid
+    // undefined behaviors.
+    uint64_t initOutputVariables : 1;
 
-// This flag works around a bug in evaluating unary minus operator on integer on some INTEL
-// drivers. It works by translating -(int) into ~(int) + 1.
-const ShCompileOptions SH_REWRITE_INTEGER_UNARY_MINUS_OPERATOR = UINT64_C(1) << 24;
+    // This flag scalarizes vec/ivec/bvec/mat constructor args.  It is intended as a workaround for
+    // Linux/Mac driver bugs.
+    uint64_t scalarizeVecAndMatConstructorArgs : 1;
 
-// This flag works around a bug in evaluating isnan() on some INTEL D3D and Mac OSX drivers.
-// It works by using an expression to emulate this function.
-const ShCompileOptions SH_EMULATE_ISNAN_FLOAT_FUNCTION = UINT64_C(1) << 25;
+    // 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.
+    uint64_t regenerateStructNames : 1;
 
-// This flag will use all uniforms of unused std140 and shared uniform blocks at the
-// beginning of the vertex/fragment shader's main(). It is intended as a workaround for Mac
-// drivers with shader version 4.10. In those drivers, they will treat unused
-// std140 and shared uniform blocks' members as inactive. However, WebGL2.0 based on
-// OpenGL ES3.0.4 requires all members of a named uniform block declared with a shared or std140
-// layout qualifier to be considered active. The uniform block itself is also considered active.
-const ShCompileOptions SH_USE_UNUSED_STANDARD_SHARED_BLOCKS = UINT64_C(1) << 26;
+    // This flag works around bugs in Mac drivers related to do-while by transforming them into an
+    // other construct.
+    uint64_t rewriteDoWhileLoops : 1;
 
-// This flag works around a bug in unary minus operator on float numbers on Intel
-// Mac OSX 10.11 drivers. It works by translating -float into 0.0 - float.
-const ShCompileOptions SH_REWRITE_FLOAT_UNARY_MINUS_OPERATOR = UINT64_C(1) << 27;
+    // 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.
+    uint64_t expandSelectHLSLIntegerPowExpressions : 1;
 
-// This flag works around a bug in evaluating atan(y, x) on some NVIDIA OpenGL drivers.
-// It works by using an expression to emulate this function.
-const ShCompileOptions SH_EMULATE_ATAN2_FLOAT_FUNCTION = UINT64_C(1) << 28;
+    // Flatten "#pragma STDGL invariant(all)" into the declarations of varying variables and
+    // built-in GLSL variables. This compiler option is enabled automatically when needed.
+    uint64_t flattenPragmaSTDGLInvariantAll : 1;
 
-// Set to initialize uninitialized local and global temporary variables. Should only be used with
-// GLSL output. In HLSL output variables are initialized regardless of if this flag is set.
-const ShCompileOptions SH_INITIALIZE_UNINITIALIZED_LOCALS = UINT64_C(1) << 29;
+    // 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.
+    uint64_t HLSLGetDimensionsIgnoresBaseLevel : 1;
 
-// The flag modifies the shader in the following way:
-// Every occurrence of gl_InstanceID is replaced by the global temporary variable InstanceID.
-// Every occurrence of gl_ViewID_OVR is replaced by the varying variable ViewID_OVR.
-// At the beginning of the body of main() in a vertex shader the following initializers are added:
-// ViewID_OVR = uint(gl_InstanceID) % num_views;
-// InstanceID = gl_InstanceID / num_views;
-// ViewID_OVR is added as a varying variable to both the vertex and fragment shaders.
-const ShCompileOptions SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW = UINT64_C(1) << 30;
+    // This flag works around an issue in translating GLSL function texelFetchOffset on INTEL
+    // drivers. It works by translating texelFetchOffset into texelFetch.
+    uint64_t rewriteTexelFetchOffsetToTexelFetch : 1;
 
-// With the flag enabled the GLSL/ESSL vertex shader is modified to include code for viewport
-// selection in the following way:
-// - Code to enable the extension ARB_shader_viewport_layer_array/NV_viewport_array2 is included.
-// - Code to select the viewport index or layer is inserted at the beginning of main after
-// ViewID_OVR's initialization.
-// - A declaration of the uniform multiviewBaseViewLayerIndex.
-// Note: The SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW flag also has to be enabled to have the
-// temporary variable ViewID_OVR declared and initialized.
-const ShCompileOptions SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER = UINT64_C(1) << 31;
+    // 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".
+    uint64_t addAndTrueToLoopCondition : 1;
 
-// If the flag is enabled, gl_PointSize is clamped to the maximum point size specified in
-// ShBuiltInResources in vertex shaders.
-const ShCompileOptions SH_CLAMP_POINT_SIZE = UINT64_C(1) << 32;
+    // This flag works around a bug in evaluating unary minus operator on integer on some INTEL
+    // drivers. It works by translating -(int) into ~(int) + 1.
+    uint64_t rewriteIntegerUnaryMinusOperator : 1;
 
-// This flag indicates whether advanced blend equation should be emulated.  Currently only
-// implemented for the Vulkan backend.
-const ShCompileOptions SH_ADD_ADVANCED_BLEND_EQUATIONS_EMULATION = UINT64_C(1) << 33;
+    // This flag works around a bug in evaluating isnan() on some INTEL D3D and Mac OSX drivers.  It
+    // works by using an expression to emulate this function.
+    uint64_t emulateIsnanFloatFunction : 1;
 
-// Don't use loops to initialize uninitialized variables. Only has an effect if some kind of
-// variable initialization is turned on.
-const ShCompileOptions SH_DONT_USE_LOOPS_TO_INITIALIZE_VARIABLES = UINT64_C(1) << 34;
+    // This flag will use all uniforms of unused std140 and shared uniform blocks at the beginning
+    // of the vertex/fragment shader's main(). It is intended as a workaround for Mac drivers with
+    // shader version 4.10. In those drivers, they will treat unused std140 and shared uniform
+    // blocks' members as inactive. However, WebGL2.0 based on OpenGL ES3.0.4 requires all members
+    // of a named uniform block declared with a shared or std140 layout qualifier to be considered
+    // active. The uniform block itself is also considered active.
+    uint64_t useUnusedStandardSharedBlocks : 1;
 
-// Don't use D3D constant register zero when allocating space for uniforms. This is targeted to work
-// around a bug in NVIDIA D3D driver version 388.59 where in very specific cases the driver would
-// not handle constant register zero correctly. Only has an effect on HLSL translation.
-const ShCompileOptions SH_SKIP_D3D_CONSTANT_REGISTER_ZERO = UINT64_C(1) << 35;
+    // This flag works around a bug in unary minus operator on float numbers on Intel Mac OSX 10.11
+    // drivers. It works by translating -float into 0.0 - float.
+    uint64_t rewriteFloatUnaryMinusOperator : 1;
 
-// Clamp gl_FragDepth to the range [0.0, 1.0] in case it is statically used.
-const ShCompileOptions SH_CLAMP_FRAG_DEPTH = UINT64_C(1) << 36;
+    // This flag works around a bug in evaluating atan(y, x) on some NVIDIA OpenGL drivers.  It
+    // works by using an expression to emulate this function.
+    uint64_t emulateAtan2FloatFunction : 1;
 
-// Rewrite expressions like "v.x = z = expression;". Works around a bug in NVIDIA OpenGL drivers
-// prior to version 397.31.
-const ShCompileOptions SH_REWRITE_REPEATED_ASSIGN_TO_SWIZZLED = UINT64_C(1) << 37;
+    // Set to initialize uninitialized local and global temporary variables. Should only be used
+    // with GLSL output. In HLSL output variables are initialized regardless of if this flag is set.
+    uint64_t initializeUninitializedLocals : 1;
 
-// Rewrite gl_DrawID as a uniform int
-const ShCompileOptions SH_EMULATE_GL_DRAW_ID = UINT64_C(1) << 38;
+    // The flag modifies the shader in the following way:
+    //
+    // Every occurrence of gl_InstanceID is replaced by the global temporary variable InstanceID.
+    // Every occurrence of gl_ViewID_OVR is replaced by the varying variable ViewID_OVR.
+    // At the beginning of the body of main() in a vertex shader the following initializers are
+    // added:
+    //   ViewID_OVR = uint(gl_InstanceID) % num_views;
+    //   InstanceID = gl_InstanceID / num_views;
+    // ViewID_OVR is added as a varying variable to both the vertex and fragment shaders.
+    uint64_t initializeBuiltinsForInstancedMultiview : 1;
 
-// This flag initializes shared variables to 0.
-// It is to avoid ompute shaders being able to read undefined values that could be coming from
-// another webpage/application.
-const ShCompileOptions SH_INIT_SHARED_VARIABLES = UINT64_C(1) << 39;
+    // With the flag enabled the GLSL/ESSL vertex shader is modified to include code for viewport
+    // selection in the following way:
+    // - Code to enable the extension ARB_shader_viewport_layer_array/NV_viewport_array2 is
+    // included.
+    // - Code to select the viewport index or layer is inserted at the beginning of main after
+    //   ViewID_OVR's initialization.
+    // - A declaration of the uniform multiviewBaseViewLayerIndex.
+    // Note: The initializeBuiltinsForInstancedMultiview flag also has to be enabled to have the
+    // temporary variable ViewID_OVR declared and initialized.
+    uint64_t selectViewInNvGLSLVertexShader : 1;
 
-// Forces the value returned from an atomic operations to be always be resolved. This is targeted to
-// workaround a bug in NVIDIA D3D driver where the return value from
-// RWByteAddressBuffer.InterlockedAdd does not get resolved when used in the .yzw components of a
-// RWByteAddressBuffer.Store operation. Only has an effect on HLSL translation.
-// http://anglebug.com/3246
-const ShCompileOptions SH_FORCE_ATOMIC_VALUE_RESOLUTION = UINT64_C(1) << 40;
+    // If the flag is enabled, gl_PointSize is clamped to the maximum point size specified in
+    // ShBuiltInResources in vertex shaders.
+    uint64_t clampPointSize : 1;
 
-// Rewrite gl_BaseVertex and gl_BaseInstance as uniform int
-const ShCompileOptions SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE = UINT64_C(1) << 41;
+    // This flag indicates whether advanced blend equation should be emulated.  Currently only
+    // implemented for the Vulkan backend.
+    uint64_t addAdvancedBlendEquationsEmulation : 1;
 
-// Emulate seamful cube map sampling for OpenGL ES2.0.  Currently only applies to the Vulkan
-// backend, as is done after samplers are moved out of structs.  Can likely be made to work on
-// the other backends as well.
-const ShCompileOptions SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING = UINT64_C(1) << 42;
+    // Don't use loops to initialize uninitialized variables. Only has an effect if some kind of
+    // variable initialization is turned on.
+    uint64_t dontUseLoopsToInitializeVariables : 1;
 
-// This flag controls how to translate WEBGL_video_texture sampling function.
-const ShCompileOptions SH_TAKE_VIDEO_TEXTURE_AS_EXTERNAL_OES = UINT64_C(1) << 43;
+    // Don't use D3D constant register zero when allocating space for uniforms. This is targeted to
+    // work around a bug in NVIDIA D3D driver version 388.59 where in very specific cases the driver
+    // would not handle constant register zero correctly. Only has an effect on HLSL translation.
+    uint64_t skipD3DConstantRegisterZero : 1;
 
-// This flag works around a inconsistent behavior in Mac AMD driver where gl_VertexID doesn't
-// include base vertex value. It replaces gl_VertexID with (gl_VertexID + angle_BaseVertex)
-// when angle_BaseVertex is available.
-const ShCompileOptions SH_ADD_BASE_VERTEX_TO_VERTEX_ID = UINT64_C(1) << 44;
+    // Clamp gl_FragDepth to the range [0.0, 1.0] in case it is statically used.
+    uint64_t clampFragDepth : 1;
 
-// This works around the dynamic lvalue indexing of swizzled vectors on various platforms.
-const ShCompileOptions SH_REMOVE_DYNAMIC_INDEXING_OF_SWIZZLED_VECTOR = UINT64_C(1) << 45;
+    // Rewrite expressions like "v.x = z = expression;". Works around a bug in NVIDIA OpenGL drivers
+    // prior to version 397.31.
+    uint64_t rewriteRepeatedAssignToSwizzled : 1;
 
-// This flag works around a slow fxc compile performance issue with dynamic uniform indexing.
-const ShCompileOptions SH_ALLOW_TRANSLATE_UNIFORM_BLOCK_TO_STRUCTUREDBUFFER = UINT64_C(1) << 46;
+    // Rewrite gl_DrawID as a uniform int
+    uint64_t emulateGLDrawID : 1;
 
-// This flag allows us to add a decoration for layout(yuv) in shaders.
-const ShCompileOptions SH_ADD_VULKAN_YUV_LAYOUT_QUALIFIER = UINT64_C(1) << 47;
+    // This flag initializes shared variables to 0.  It is to avoid ompute shaders being able to
+    // read undefined values that could be coming from another webpage/application.
+    uint64_t initSharedVariables : 1;
 
-// This flag allows disabling ARB_texture_rectangle on a per-compile basis. This is necessary
-// for WebGL contexts becuase ARB_texture_rectangle may be necessary for the WebGL implementation
-// internally but shouldn't be exposed to WebGL user code.
-const ShCompileOptions SH_DISABLE_ARB_TEXTURE_RECTANGLE = UINT64_C(1) << 48;
+    // Forces the value returned from an atomic operations to be always be resolved. This is
+    // targeted to workaround a bug in NVIDIA D3D driver where the return value from
+    // RWByteAddressBuffer.InterlockedAdd does not get resolved when used in the .yzw components of
+    // a RWByteAddressBuffer.Store operation. Only has an effect on HLSL translation.
+    // http://anglebug.com/3246
+    uint64_t forceAtomicValueResolution : 1;
 
-// This flag works around a driver bug by rewriting uses of row-major matrices
-// as column-major in ESSL 3.00 and greater shaders.
-const ShCompileOptions SH_REWRITE_ROW_MAJOR_MATRICES = UINT64_C(1) << 49;
+    // Rewrite gl_BaseVertex and gl_BaseInstance as uniform int
+    uint64_t emulateGLBaseVertexBaseInstance : 1;
 
-// Drop any explicit precision qualifiers from shader.
-const ShCompileOptions SH_IGNORE_PRECISION_QUALIFIERS = UINT64_C(1) << 50;
+    // Emulate seamful cube map sampling for OpenGL ES2.0.  Currently only applies to the Vulkan
+    // backend, as is done after samplers are moved out of structs.  Can likely be made to work on
+    // the other backends as well.
+    uint64_t emulateSeamfulCubeMapSampling : 1;
 
-// Ask compiler to generate code for depth correction to conform to the Vulkan clip space.  If
-// VK_EXT_depth_clip_control is supported, this code is not generated, saving a uniform look up.
-const ShCompileOptions SH_ADD_VULKAN_DEPTH_CORRECTION = UINT64_C(1) << 51;
+    // This flag controls how to translate WEBGL_video_texture sampling function.
+    uint64_t takeVideoTextureAsExternalOES : 1;
 
-// Note: bit 52 is unused
+    // This flag works around a inconsistent behavior in Mac AMD driver where gl_VertexID doesn't
+    // include base vertex value. It replaces gl_VertexID with (gl_VertexID + angle_BaseVertex) when
+    // angle_BaseVertex is available.
+    uint64_t addBaseVertexToVertexID : 1;
 
-const ShCompileOptions SH_FORCE_SHADER_PRECISION_HIGHP_TO_MEDIUMP = UINT64_C(1) << 53;
+    // This works around the dynamic lvalue indexing of swizzled vectors on various platforms.
+    uint64_t removeDynamicIndexingOfSwizzledVector : 1;
 
-// Allow compiler to use specialization constant to do pre-rotation and y flip.
-const ShCompileOptions SH_USE_SPECIALIZATION_CONSTANT = UINT64_C(1) << 54;
+    // This flag works around a slow fxc compile performance issue with dynamic uniform indexing.
+    uint64_t allowTranslateUniformBlockToStructuredBuffer : 1;
 
-// Ask compiler to generate Vulkan transform feedback emulation support code.
-const ShCompileOptions SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE = UINT64_C(1) << 55;
+    // This flag allows us to add a decoration for layout(yuv) in shaders.
+    uint64_t addVulkanYUVLayoutQualifier : 1;
 
-// Ask compiler to generate Vulkan transform feedback support code when using the
-// VK_EXT_transform_feedback extension.
-const ShCompileOptions SH_ADD_VULKAN_XFB_EXTENSION_SUPPORT_CODE = UINT64_C(1) << 56;
+    // This flag allows disabling ARB_texture_rectangle on a per-compile basis. This is necessary
+    // for WebGL contexts becuase ARB_texture_rectangle may be necessary for the WebGL
+    // implementation internally but shouldn't be exposed to WebGL user code.
+    uint64_t disableARBTextureRectangle : 1;
 
-// This flag initializes fragment shader's output variables to zero at the beginning of the fragment
-// shader's main(). It is intended as a workaround for drivers which get context lost if
-// gl_FragColor is not written.
-const ShCompileOptions SH_INIT_FRAGMENT_OUTPUT_VARIABLES = UINT64_C(1) << 57;
+    // This flag works around a driver bug by rewriting uses of row-major matrices as column-major
+    // in ESSL 3.00 and greater shaders.
+    uint64_t rewriteRowMajorMatrices : 1;
 
-// Transitory flag to select between producing SPIR-V directly vs using glslang.  Ignored in
-// non-assert-enabled builds to avoid increasing ANGLE's binary size.
-const ShCompileOptions SH_GENERATE_SPIRV_THROUGH_GLSLANG = UINT64_C(1) << 58;
+    // Drop any explicit precision qualifiers from shader.
+    uint64_t ignorePrecisionQualifiers : 1;
 
-// Insert explicit casts for float/double/unsigned/signed int on macOS 10.15 with Intel driver
-const ShCompileOptions SH_ADD_EXPLICIT_BOOL_CASTS = UINT64_C(1) << 59;
+    // Ask compiler to generate code for depth correction to conform to the Vulkan clip space.  If
+    // VK_EXT_depth_clip_control is supported, this code is not generated, saving a uniform look up.
+    uint64_t addVulkanDepthCorrection : 1;
 
-// Add round() after applying dither.  This works around a Qualcomm quirk where values can get
-// ceil()ed instead.
-const ShCompileOptions SH_ROUND_OUTPUT_AFTER_DITHERING = UINT64_C(1) << 60;
+    uint64_t forceShaderPrecisionHighpToMediump : 1;
 
-// Even when the dividend and divisor have the same value some platforms do not return 1.0f.
-// Need to emit different division code for such platforms.
-const ShCompileOptions SH_PRECISION_SAFE_DIVISION = UINT64_C(1) << 61;
+    // Allow compiler to use specialization constant to do pre-rotation and y flip.
+    uint64_t useSpecializationConstant : 1;
+
+    // Ask compiler to generate Vulkan transform feedback emulation support code.
+    uint64_t addVulkanXfbEmulationSupportCode : 1;
+
+    // Ask compiler to generate Vulkan transform feedback support code when using the
+    // VK_EXT_transform_feedback extension.
+    uint64_t addVulkanXfbExtensionSupportCode : 1;
+
+    // This flag initializes fragment shader's output variables to zero at the beginning of the
+    // fragment shader's main(). It is intended as a workaround for drivers which get context lost
+    // if gl_FragColor is not written.
+    uint64_t initFragmentOutputVariables : 1;
+
+    // Transitory flag to select between producing SPIR-V directly vs using glslang.  Ignored in
+    // non-assert-enabled builds to avoid increasing ANGLE's binary size.
+    uint64_t generateSpirvThroughGlslang : 1;
+
+    // Insert explicit casts for float/double/unsigned/signed int on macOS 10.15 with Intel driver
+    uint64_t addExplicitBoolCasts : 1;
+
+    // Add round() after applying dither.  This works around a Qualcomm quirk where values can get
+    // ceil()ed instead.
+    uint64_t roundOutputAfterDithering : 1;
+
+    // Even when the dividend and divisor have the same value some platforms do not return 1.0f.
+    // Need to emit different division code for such platforms.
+    uint64_t precisionSafeDivision : 1;
+
+    ShCompileOptionsMetal metal;
+    ShCompileOptionsPLS pls;
+};
 
 // The 64 bits hash function. The first parameter is the input string; the
 // second parameter is the string length.
@@ -472,14 +490,14 @@
     // Default is NULL.
     ShHashFunction64 HashFunction;
 
-    // The maximum complexity an expression can be when SH_LIMIT_EXPRESSION_COMPLEXITY is turned on.
+    // The maximum complexity an expression can be when limitExpressionComplexity is turned on.
     int MaxExpressionComplexity;
 
     // The maximum depth a call stack can be.
     int MaxCallStackDepth;
 
-    // The maximum number of parameters a function can have when SH_LIMIT_EXPRESSION_COMPLEXITY is
-    // turned on.
+    // The maximum number of parameters a function can have when limitExpressionComplexity is turned
+    // on.
     int MaxFunctionParameters;
 
     // GLES 3.1 constants
@@ -606,18 +624,6 @@
     int MaxClipDistances;
     int MaxCullDistances;
     int MaxCombinedClipAndCullDistances;
-
-    // Direct-to-metal backend constants:
-
-    // Binding index for driver uniforms:
-    int DriverUniformsBindingIndex;
-    // Binding index for default uniforms:
-    int DefaultUniformsBindingIndex;
-    // Binding index for UBO's argument buffer
-    int UBOArgumentBufferBindingIndex;
-
-    // For ANGLE_shader_pixel_local_storage_coherent.
-    ShFragmentSynchronizationType FragmentSynchronizationType;
 };
 
 //
@@ -687,7 +693,7 @@
 bool Compile(const ShHandle handle,
              const char *const shaderStrings[],
              size_t numStrings,
-             ShCompileOptions compileOptions);
+             const ShCompileOptions &compileOptions);
 
 // Clears the results from the previous compilation.
 void ClearResults(const ShHandle handle);
@@ -749,8 +755,7 @@
 
 // Returns true if the passed in variables pack in maxVectors followingthe packing rules from the
 // GLSL 1.017 spec, Appendix A, section 7.
-// Returns false otherwise. Also look at the SH_ENFORCE_PACKING_RESTRICTIONS
-// flag above.
+// Returns false otherwise. Also look at the enforcePackingRestrictions flag above.
 // Parameters:
 // maxVectors: the available rows of registers.
 // variables: an array of variables.
diff --git a/include/GLSLANG/ShaderVars.h b/include/GLSLANG/ShaderVars.h
index 18175e1..ac1cd5e 100644
--- a/include/GLSLANG/ShaderVars.h
+++ b/include/GLSLANG/ShaderVars.h
@@ -15,9 +15,6 @@
 #include <string>
 #include <vector>
 
-// This type is defined here to simplify ANGLE's integration with glslang for SPIR-V.
-using ShCompileOptions = uint64_t;
-
 namespace sh
 {
 // GLenum alias
diff --git a/samples/shader_translator/shader_translator.cpp b/samples/shader_translator/shader_translator.cpp
index a5916d4..cd141ee 100644
--- a/samples/shader_translator/shader_translator.cpp
+++ b/samples/shader_translator/shader_translator.cpp
@@ -33,7 +33,7 @@
 
 static void usage();
 static sh::GLenum FindShaderType(const char *fileName);
-static bool CompileFile(char *fileName, ShHandle compiler, ShCompileOptions compileOptions);
+static bool CompileFile(char *fileName, ShHandle compiler, const ShCompileOptions &compileOptions);
 static void LogMsg(const char *msg, const char *name, const int num, const char *logName);
 static void PrintVariable(const std::string &prefix, size_t index, const sh::ShaderVariable &var);
 static void PrintActiveVariables(ShHandle compiler);
@@ -79,7 +79,7 @@
 {
     TFailCode failCode = ESuccess;
 
-    ShCompileOptions compileOptions = 0;
+    ShCompileOptions compileOptions = {};
     int numCompiles                 = 0;
     ShHandle vertexCompiler         = 0;
     ShHandle fragmentCompiler       = 0;
@@ -105,13 +105,13 @@
             switch (argv[0][1])
             {
                 case 'i':
-                    compileOptions |= SH_INTERMEDIATE_TREE;
+                    compileOptions.intermediateTree = true;
                     break;
                 case 'o':
-                    compileOptions |= SH_OBJECT_CODE;
+                    compileOptions.objectCode = true;
                     break;
                 case 'u':
-                    compileOptions |= SH_VARIABLES;
+                    compileOptions.variables = true;
                     break;
                 case 's':
                     if (argv[0][2] == '=')
@@ -179,19 +179,19 @@
                         switch (argv[0][3])
                         {
                             case 'e':
-                                output = SH_ESSL_OUTPUT;
-                                compileOptions |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
+                                output                                       = SH_ESSL_OUTPUT;
+                                compileOptions.initializeUninitializedLocals = true;
                                 break;
                             case 'g':
                                 if (!ParseGLSLOutputVersion(&argv[0][sizeof("-b=g") - 1], &output))
                                 {
                                     failCode = EFailUsage;
                                 }
-                                compileOptions |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
+                                compileOptions.initializeUninitializedLocals = true;
                                 break;
                             case 'v':
                                 output = SH_SPIRV_VULKAN_OUTPUT;
-                                compileOptions |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
+                                compileOptions.initializeUninitializedLocals = true;
                                 break;
                             case 'h':
                                 if (argv[0][4] == '1' && argv[0][5] == '1')
@@ -261,8 +261,8 @@
                       case 'm':
                           resources.OVR_multiview2 = 1;
                           resources.OVR_multiview = 1;
-                          compileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
-                          compileOptions |= SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER;
+                          compileOptions.initializeBuiltinsForInstancedMultiview = true;
+                          compileOptions.selectViewInNvGLSLVertexShader = true;
                           break;
                       case 'y': resources.EXT_YUV_target = 1; break;
                       case 's': resources.OES_sample_variables = 1; break;
@@ -332,7 +332,7 @@
                     case SH_HLSL_3_0_OUTPUT:
                     case SH_HLSL_4_1_OUTPUT:
                     case SH_HLSL_4_0_FL9_3_OUTPUT:
-                        compileOptions &= ~SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER;
+                        compileOptions.selectViewInNvGLSLVertexShader = false;
                         break;
                     default:
                         break;
@@ -346,7 +346,7 @@
                 LogMsg("END", "COMPILER", numCompiles, "INFO LOG");
                 printf("\n\n");
 
-                if (compiled && (compileOptions & SH_OBJECT_CODE))
+                if (compiled && compileOptions.objectCode)
                 {
                     LogMsg("BEGIN", "COMPILER", numCompiles, "OBJ CODE");
                     if (output != SH_SPIRV_VULKAN_OUTPUT)
@@ -362,7 +362,7 @@
                     LogMsg("END", "COMPILER", numCompiles, "OBJ CODE");
                     printf("\n\n");
                 }
-                if (compiled && (compileOptions & SH_VARIABLES))
+                if (compiled && compileOptions.variables)
                 {
                     LogMsg("BEGIN", "COMPILER", numCompiles, "VARIABLES");
                     PrintActiveVariables(compiler);
@@ -484,7 +484,7 @@
 //
 //   Read a file's data into a string, and compile it using sh::Compile
 //
-bool CompileFile(char *fileName, ShHandle compiler, ShCompileOptions compileOptions)
+bool CompileFile(char *fileName, ShHandle compiler, const ShCompileOptions &compileOptions)
 {
     ShaderSource source;
     if (!ReadShaderSource(fileName, source))
diff --git a/src/compiler/fuzz/translator_fuzzer.cpp b/src/compiler/fuzz/translator_fuzzer.cpp
index c3378f7..8e44e47 100644
--- a/src/compiler/fuzz/translator_fuzzer.cpp
+++ b/src/compiler/fuzz/translator_fuzzer.cpp
@@ -21,48 +21,6 @@
 
 namespace
 {
-
-// Options supported by any output
-constexpr ShCompileOptions kCommonOptions =
-    SH_VALIDATE_LOOP_INDEXING | SH_INTERMEDIATE_TREE | SH_OBJECT_CODE | SH_VARIABLES |
-    SH_LINE_DIRECTIVES | SH_SOURCE_PATH | SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3 |
-    SH_EMULATE_ABS_INT_FUNCTION | SH_ENFORCE_PACKING_RESTRICTIONS | SH_CLAMP_INDIRECT_ARRAY_BOUNDS |
-    SH_LIMIT_EXPRESSION_COMPLEXITY | SH_LIMIT_CALL_STACK_DEPTH | SH_INIT_GL_POSITION |
-    SH_INIT_OUTPUT_VARIABLES | SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS |
-    SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL | SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL |
-    SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH | SH_EMULATE_ISNAN_FLOAT_FUNCTION |
-    SH_INITIALIZE_UNINITIALIZED_LOCALS | SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-    SH_CLAMP_POINT_SIZE | SH_DONT_USE_LOOPS_TO_INITIALIZE_VARIABLES |
-    SH_SKIP_D3D_CONSTANT_REGISTER_ZERO | SH_EMULATE_GL_DRAW_ID | SH_INIT_SHARED_VARIABLES |
-    SH_FORCE_ATOMIC_VALUE_RESOLUTION | SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE |
-    SH_TAKE_VIDEO_TEXTURE_AS_EXTERNAL_OES | SH_VALIDATE_AST | SH_ADD_BASE_VERTEX_TO_VERTEX_ID |
-    SH_REMOVE_DYNAMIC_INDEXING_OF_SWIZZLED_VECTOR | SH_DISABLE_ARB_TEXTURE_RECTANGLE |
-    SH_IGNORE_PRECISION_QUALIFIERS | SH_FORCE_SHADER_PRECISION_HIGHP_TO_MEDIUMP;
-
-// Options supported by GLSL or ESSL only
-constexpr ShCompileOptions kGLSLOrESSLOnlyOptions =
-    SH_EMULATE_ATAN2_FLOAT_FUNCTION | SH_CLAMP_FRAG_DEPTH | SH_REGENERATE_STRUCT_NAMES |
-    SH_REWRITE_REPEATED_ASSIGN_TO_SWIZZLED | SH_USE_UNUSED_STANDARD_SHARED_BLOCKS |
-    SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER;
-
-#if defined(ANGLE_PLATFORM_APPLE)
-// Options supported by GLSL only on mac
-constexpr ShCompileOptions kGLSLMacOnlyOptions =
-    SH_REWRITE_FLOAT_UNARY_MINUS_OPERATOR | SH_ADD_AND_TRUE_TO_LOOP_CONDITION |
-    SH_REWRITE_DO_WHILE_LOOPS | SH_UNFOLD_SHORT_CIRCUIT | SH_REWRITE_ROW_MAJOR_MATRICES;
-#endif
-
-// Options supported by Vulkan SPIR-V output only
-constexpr ShCompileOptions kVulkanOnlyOptions =
-    SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING | SH_USE_SPECIALIZATION_CONSTANT |
-    SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE | SH_ROUND_OUTPUT_AFTER_DITHERING |
-    SH_ADD_ADVANCED_BLEND_EQUATIONS_EMULATION;
-
-// Options supported by HLSL output only
-constexpr ShCompileOptions kHLSLOnlyOptions = SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS |
-                                              SH_ALLOW_TRANSLATE_UNIFORM_BLOCK_TO_STRUCTUREDBUFFER |
-                                              SH_REWRITE_INTEGER_UNARY_MINUS_OPERATOR;
-
 struct TranslatorCacheKey
 {
     bool operator==(const TranslatorCacheKey &other) const
@@ -111,10 +69,10 @@
         return 0;
     }
 
-    uint32_t type    = *reinterpret_cast<const uint32_t *>(data);
-    uint32_t spec    = *reinterpret_cast<const uint32_t *>(data + 4);
-    uint32_t output  = *reinterpret_cast<const uint32_t *>(data + 8);
-    uint64_t options = *reinterpret_cast<const uint64_t *>(data + 12);
+    uint32_t type            = *reinterpret_cast<const uint32_t *>(data);
+    uint32_t spec            = *reinterpret_cast<const uint32_t *>(data + 4);
+    uint32_t output          = *reinterpret_cast<const uint32_t *>(data + 8);
+    ShCompileOptions options = *reinterpret_cast<const ShCompileOptions *>(data + 12);
 
     if (type != GL_FRAGMENT_SHADER && type != GL_VERTEX_SHADER)
     {
@@ -129,30 +87,47 @@
 
     ShShaderOutput shaderOutput = static_cast<ShShaderOutput>(output);
 
-    ShCompileOptions supportedOptions = kCommonOptions;
+    bool hasUnsupportedOptions = false;
 
-    if (IsOutputGLSL(shaderOutput) || IsOutputESSL(shaderOutput))
+    if (!IsOutputGLSL(shaderOutput) && !IsOutputESSL(shaderOutput))
     {
-        supportedOptions |= kGLSLOrESSLOnlyOptions;
-#if defined(ANGLE_PLATFORM_APPLE)
-        supportedOptions |= kGLSLMacOnlyOptions;
+        hasUnsupportedOptions =
+            hasUnsupportedOptions || options.emulateAtan2FloatFunction || options.clampFragDepth ||
+            options.regenerateStructNames || options.rewriteRepeatedAssignToSwizzled ||
+            options.useUnusedStandardSharedBlocks || options.selectViewInNvGLSLVertexShader;
+
+#if !defined(ANGLE_PLATFORM_APPLE)
+        hasUnsupportedOptions = hasUnsupportedOptions || options.rewriteFloatUnaryMinusOperator ||
+                                options.addAndTrueToLoopCondition || options.rewriteDoWhileLoops ||
+                                options.unfoldShortCircuit || options.rewriteRowMajorMatrices;
 #endif
     }
-    else if (IsOutputVulkan(shaderOutput))
+    if (!IsOutputVulkan(shaderOutput))
     {
-        supportedOptions |= kVulkanOnlyOptions;
+        hasUnsupportedOptions =
+            hasUnsupportedOptions || options.emulateSeamfulCubeMapSampling ||
+            options.useSpecializationConstant || options.addVulkanXfbEmulationSupportCode ||
+            options.roundOutputAfterDithering || options.addAdvancedBlendEquationsEmulation;
     }
-    else if (IsOutputHLSL(shaderOutput))
+    if (!IsOutputHLSL(shaderOutput))
     {
-        supportedOptions |= kHLSLOnlyOptions;
+        hasUnsupportedOptions = hasUnsupportedOptions ||
+                                options.expandSelectHLSLIntegerPowExpressions ||
+                                options.allowTranslateUniformBlockToStructuredBuffer ||
+                                options.rewriteIntegerUnaryMinusOperator;
     }
 
     // If there are any options not supported with this output, don't attempt to run the translator.
-    if ((options & ~supportedOptions) != 0)
+    if (hasUnsupportedOptions)
     {
         return 0;
     }
 
+    // Make sure the rest of the options are in a valid range.
+    options.pls.fragmentSynchronizationType = static_cast<ShFragmentSynchronizationType>(
+        static_cast<uint32_t>(options.pls.fragmentSynchronizationType) %
+        static_cast<uint32_t>(ShFragmentSynchronizationType::InvalidEnum));
+
     std::vector<uint32_t> validOutputs;
     validOutputs.push_back(SH_ESSL_OUTPUT);
     validOutputs.push_back(SH_GLSL_COMPATIBILITY_OUTPUT);
diff --git a/src/compiler/translator/BuildSPIRV.cpp b/src/compiler/translator/BuildSPIRV.cpp
index 1f25b56..8f996a2 100644
--- a/src/compiler/translator/BuildSPIRV.cpp
+++ b/src/compiler/translator/BuildSPIRV.cpp
@@ -479,7 +479,7 @@
 }
 
 SPIRVBuilder::SPIRVBuilder(TCompiler *compiler,
-                           ShCompileOptions compileOptions,
+                           const ShCompileOptions &compileOptions,
                            ShHashFunction64 hashFunction,
                            NameMap &nameMap)
     : mCompiler(compiler),
@@ -667,7 +667,7 @@
 
 SpirvDecorations SPIRVBuilder::getDecorations(const TType &type)
 {
-    const bool enablePrecision = (mCompileOptions & SH_IGNORE_PRECISION_QUALIFIERS) == 0;
+    const bool enablePrecision = !mCompileOptions.ignorePrecisionQualifiers;
     const TPrecision precision = type.getPrecision();
 
     SpirvDecorations decorations;
@@ -1746,7 +1746,7 @@
     const bool needsInputAttachmentIndex = IsSubpassInputType(type.getBasicType());
     const bool needsBlendIndex =
         type.getQualifier() == EvqFragmentOut && layoutQualifier.index >= 0;
-    const bool needsYuvDecorate = (mCompileOptions & SH_ADD_VULKAN_YUV_LAYOUT_QUALIFIER) != 0 &&
+    const bool needsYuvDecorate = mCompileOptions.addVulkanYUVLayoutQualifier &&
                                   type.getQualifier() == EvqFragmentOut && layoutQualifier.yuv;
 
     // If the resource declaration requires set & binding, add the DescriptorSet and Binding
diff --git a/src/compiler/translator/BuildSPIRV.h b/src/compiler/translator/BuildSPIRV.h
index 602d058..8839b28 100644
--- a/src/compiler/translator/BuildSPIRV.h
+++ b/src/compiler/translator/BuildSPIRV.h
@@ -303,7 +303,7 @@
 {
   public:
     SPIRVBuilder(TCompiler *compiler,
-                 ShCompileOptions compileOptions,
+                 const ShCompileOptions &compileOptions,
                  ShHashFunction64 hashFunction,
                  NameMap &nameMap);
 
@@ -463,7 +463,7 @@
     void writeSourceExtensions(spirv::Blob *blob);
 
     [[maybe_unused]] TCompiler *mCompiler;
-    ShCompileOptions mCompileOptions;
+    const ShCompileOptions &mCompileOptions;
     gl::ShaderType mShaderType;
 
     // Capabilities the shader is using.  Accumulated as the instructions are generated.  The Shader
diff --git a/src/compiler/translator/BuiltinsWorkaroundGLSL.cpp b/src/compiler/translator/BuiltinsWorkaroundGLSL.cpp
index 7b8eaef..6409f85 100644
--- a/src/compiler/translator/BuiltinsWorkaroundGLSL.cpp
+++ b/src/compiler/translator/BuiltinsWorkaroundGLSL.cpp
@@ -22,7 +22,7 @@
 class TBuiltinsWorkaroundGLSL : public TIntermTraverser
 {
   public:
-    TBuiltinsWorkaroundGLSL(TSymbolTable *symbolTable, ShCompileOptions options);
+    TBuiltinsWorkaroundGLSL(TSymbolTable *symbolTable, const ShCompileOptions &options);
 
     void visitSymbol(TIntermSymbol *node) override;
     bool visitDeclaration(Visit, TIntermDeclaration *node) override;
@@ -30,13 +30,13 @@
   private:
     void ensureVersionIsAtLeast(int version);
 
-    ShCompileOptions mCompileOptions;
+    const ShCompileOptions &mCompileOptions;
 
     bool isBaseInstanceDeclared = false;
 };
 
 TBuiltinsWorkaroundGLSL::TBuiltinsWorkaroundGLSL(TSymbolTable *symbolTable,
-                                                 ShCompileOptions options)
+                                                 const ShCompileOptions &options)
     : TIntermTraverser(true, false, false, symbolTable), mCompileOptions(options)
 {}
 
@@ -95,7 +95,7 @@
 [[nodiscard]] bool ShaderBuiltinsWorkaround(TCompiler *compiler,
                                             TIntermBlock *root,
                                             TSymbolTable *symbolTable,
-                                            ShCompileOptions compileOptions)
+                                            const ShCompileOptions &compileOptions)
 {
     TBuiltinsWorkaroundGLSL builtins(symbolTable, compileOptions);
     root->traverse(&builtins);
diff --git a/src/compiler/translator/BuiltinsWorkaroundGLSL.h b/src/compiler/translator/BuiltinsWorkaroundGLSL.h
index 9e71a06..e49273d 100644
--- a/src/compiler/translator/BuiltinsWorkaroundGLSL.h
+++ b/src/compiler/translator/BuiltinsWorkaroundGLSL.h
@@ -17,7 +17,7 @@
 [[nodiscard]] bool ShaderBuiltinsWorkaround(TCompiler *compiler,
                                             TIntermBlock *root,
                                             TSymbolTable *symbolTable,
-                                            ShCompileOptions compileOptions);
+                                            const ShCompileOptions &compileOptions);
 
 }  // namespace sh
 
diff --git a/src/compiler/translator/CollectVariables.cpp b/src/compiler/translator/CollectVariables.cpp
index cf73baa..e75811d 100644
--- a/src/compiler/translator/CollectVariables.cpp
+++ b/src/compiler/translator/CollectVariables.cpp
@@ -573,7 +573,7 @@
                 recordBuiltInAttributeUsed(symbol->variable(), &mLocalInvocationIndexAdded);
                 return;
             case EvqInstanceID:
-                // Whenever the SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW option is set,
+                // Whenever the initializeBuiltinsForInstancedMultiview option is set,
                 // gl_InstanceID is added inside expressions to initialize ViewID_OVR and
                 // InstanceID. Note that gl_InstanceID is not added to the symbol table for ESSL1
                 // shaders.
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index ac2fd5c..12644d7 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -158,13 +158,13 @@
 bool RemoveInvariant(sh::GLenum shaderType,
                      int shaderVersion,
                      ShShaderOutput outputType,
-                     ShCompileOptions compileOptions)
+                     const ShCompileOptions &compileOptions)
 {
     if (shaderType == GL_FRAGMENT_SHADER && IsGLSL420OrNewer(outputType))
         return true;
 
-    if ((compileOptions & SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3) != 0 &&
-        shaderVersion >= 300 && shaderType == GL_VERTEX_SHADER)
+    if (compileOptions.removeInvariantAndCentroidForESSL3 && shaderVersion >= 300 &&
+        shaderType == GL_VERTEX_SHADER)
         return true;
 
     return false;
@@ -347,7 +347,7 @@
       mTessEvaluationShaderInputPointType(EtetUndefined),
       mHasAnyPreciseType(false),
       mAdvancedBlendEquations(0),
-      mCompileOptions(0)
+      mCompileOptions{}
 {}
 
 TCompiler::~TCompiler() {}
@@ -358,13 +358,13 @@
            mResources.FragmentPrecisionHigh == 1;
 }
 
-bool TCompiler::shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const
+bool TCompiler::shouldRunLoopAndIndexingValidation(const ShCompileOptions &compileOptions) const
 {
     // If compiling an ESSL 1.00 shader for WebGL, or if its been requested through the API,
     // validate loop and indexing as well (to verify that the shader only uses minimal functionality
     // of ESSL 1.00 as in Appendix A of the spec).
     return (IsWebGLBasedSpec(mShaderSpec) && mShaderVersion == 100) ||
-           (compileOptions & SH_VALIDATE_LOOP_INDEXING) != 0;
+           compileOptions.validateLoopIndexing;
 }
 
 bool TCompiler::shouldLimitTypeSizes() const
@@ -391,14 +391,14 @@
 
 TIntermBlock *TCompiler::compileTreeForTesting(const char *const shaderStrings[],
                                                size_t numStrings,
-                                               ShCompileOptions compileOptions)
+                                               const ShCompileOptions &compileOptions)
 {
     return compileTreeImpl(shaderStrings, numStrings, compileOptions);
 }
 
 TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
                                          size_t numStrings,
-                                         const ShCompileOptions compileOptions)
+                                         const ShCompileOptions &compileOptions)
 {
     // Remember the compile options for helper functions such as validateAST.
     mCompileOptions = compileOptions;
@@ -413,7 +413,7 @@
 
     // If gl_DrawID is not supported, remove it from the available extensions
     // Currently we only allow emulation of gl_DrawID
-    const bool glDrawIDSupported = (compileOptions & SH_EMULATE_GL_DRAW_ID) != 0;
+    const bool glDrawIDSupported = compileOptions.emulateGLDrawID;
     if (!glDrawIDSupported)
     {
         auto it = mExtensionBehavior.find(TExtension::ANGLE_multi_draw);
@@ -423,8 +423,7 @@
         }
     }
 
-    const bool glBaseVertexBaseInstanceSupported =
-        (compileOptions & SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE) != 0;
+    const bool glBaseVertexBaseInstanceSupported = compileOptions.emulateGLBaseVertexBaseInstance;
     if (!glBaseVertexBaseInstanceSupported)
     {
         auto it =
@@ -437,7 +436,7 @@
 
     // First string is path of source file if flag is set. The actual source follows.
     size_t firstSource = 0;
-    if ((compileOptions & SH_SOURCE_PATH) != 0)
+    if (compileOptions.sourcePath)
     {
         mSourcePath = shaderStrings[0];
         ++firstSource;
@@ -603,7 +602,7 @@
 
 bool TCompiler::validateAST(TIntermNode *root)
 {
-    if ((mCompileOptions & SH_VALIDATE_AST) != 0)
+    if (mCompileOptions.validateAST)
     {
         bool valid = ValidateAST(root, &mDiagnostics, mValidateASTOptions);
 
@@ -656,7 +655,7 @@
 
 bool TCompiler::checkAndSimplifyAST(TIntermBlock *root,
                                     const TParseContext &parseContext,
-                                    ShCompileOptions compileOptions)
+                                    const ShCompileOptions &compileOptions)
 {
     mValidateASTOptions = {};
 
@@ -690,7 +689,7 @@
     }
 
     // Disallow expressions deemed too complex.
-    if ((compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY) != 0 && !limitExpressionComplexity(root))
+    if (compileOptions.limitExpressionComplexity && !limitExpressionComplexity(root))
     {
         return false;
     }
@@ -741,10 +740,9 @@
     mValidateASTOptions.validateNoStatementsAfterBranch = true;
 
     // We need to generate globals early if we have non constant initializers enabled
-    bool initializeLocalsAndGlobals = (compileOptions & SH_INITIALIZE_UNINITIALIZED_LOCALS) != 0 &&
-                                      !IsOutputHLSL(getOutputType());
-    bool canUseLoopsToInitialize =
-        (compileOptions & SH_DONT_USE_LOOPS_TO_INITIALIZE_VARIABLES) == 0;
+    bool initializeLocalsAndGlobals =
+        compileOptions.initializeUninitializedLocals && !IsOutputHLSL(getOutputType());
+    bool canUseLoopsToInitialize       = !compileOptions.dontUseLoopsToInitializeVariables;
     bool highPrecisionSupported        = isHighPrecisionSupported();
     bool enableNonConstantInitializers = IsExtensionEnabled(
         mExtensionBehavior, TExtension::EXT_shader_non_constant_global_initializers);
@@ -776,7 +774,7 @@
         return false;
     }
 
-    if ((compileOptions & SH_LIMIT_CALL_STACK_DEPTH) != 0 && !checkCallDepth())
+    if (compileOptions.limitCallStackDepth && !checkCallDepth())
     {
         return false;
     }
@@ -823,7 +821,7 @@
     }
 
     // Clamping uniform array bounds needs to happen after validateLimitations pass.
-    if ((compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS) != 0)
+    if (compileOptions.clampIndirectArrayBounds)
     {
         if (!ClampIndirectIndices(this, root, &mSymbolTable))
         {
@@ -831,7 +829,7 @@
         }
     }
 
-    if ((compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) != 0 &&
+    if (compileOptions.initializeBuiltinsForInstancedMultiview &&
         (parseContext.isExtensionEnabled(TExtension::OVR_multiview2) ||
          parseContext.isExtensionEnabled(TExtension::OVR_multiview)) &&
         getShaderType() != GL_COMPUTE_SHADER)
@@ -844,7 +842,7 @@
     }
 
     // This pass might emit short circuits so keep it before the short circuit unfolding
-    if ((compileOptions & SH_REWRITE_DO_WHILE_LOOPS) != 0)
+    if (compileOptions.rewriteDoWhileLoops)
     {
         if (!RewriteDoWhile(this, root, &mSymbolTable))
         {
@@ -852,7 +850,7 @@
         }
     }
 
-    if ((compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION) != 0)
+    if (compileOptions.addAndTrueToLoopCondition)
     {
         if (!AddAndTrueToLoopCondition(this, root))
         {
@@ -860,7 +858,7 @@
         }
     }
 
-    if ((compileOptions & SH_UNFOLD_SHORT_CIRCUIT) != 0)
+    if (compileOptions.unfoldShortCircuit)
     {
         if (!UnfoldShortCircuitAST(this, root))
         {
@@ -868,7 +866,7 @@
         }
     }
 
-    if ((compileOptions & SH_REGENERATE_STRUCT_NAMES) != 0)
+    if (compileOptions.regenerateStructNames)
     {
         if (!RegenerateStructNames(this, root, &mSymbolTable))
         {
@@ -879,7 +877,7 @@
     if (mShaderType == GL_VERTEX_SHADER &&
         IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_multi_draw))
     {
-        if ((compileOptions & SH_EMULATE_GL_DRAW_ID) != 0)
+        if (compileOptions.emulateGLDrawID)
         {
             if (!EmulateGLDrawID(this, root, &mSymbolTable, &mUniforms,
                                  shouldCollectVariables(compileOptions)))
@@ -893,11 +891,11 @@
         IsExtensionEnabled(mExtensionBehavior,
                            TExtension::ANGLE_base_vertex_base_instance_shader_builtin))
     {
-        if ((compileOptions & SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE) != 0)
+        if (compileOptions.emulateGLBaseVertexBaseInstance)
         {
-            if (!EmulateGLBaseVertexBaseInstance(
-                    this, root, &mSymbolTable, &mUniforms, shouldCollectVariables(compileOptions),
-                    (compileOptions & SH_ADD_BASE_VERTEX_TO_VERTEX_ID) != 0))
+            if (!EmulateGLBaseVertexBaseInstance(this, root, &mSymbolTable, &mUniforms,
+                                                 shouldCollectVariables(compileOptions),
+                                                 compileOptions.addBaseVertexToVertexID))
             {
                 return false;
             }
@@ -915,7 +913,7 @@
         }
     }
 
-    int simplifyScalarized = (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS) != 0
+    int simplifyScalarized = compileOptions.scalarizeVecAndMatConstructorArgs
                                  ? IntermNodePatternMatcher::kScalarizedVecOrMatConstructor
                                  : 0;
 
@@ -973,7 +971,7 @@
     GetGlobalPoolAllocator()->unlock();
     mBuiltInFunctionEmulator.markBuiltInFunctionsForEmulation(root);
 
-    if ((compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS) != 0)
+    if (compileOptions.scalarizeVecAndMatConstructorArgs)
     {
         if (!ScalarizeVecAndMatConstructorArgs(this, root, &mSymbolTable))
         {
@@ -981,7 +979,7 @@
         }
     }
 
-    if ((compileOptions & SH_FORCE_SHADER_PRECISION_HIGHP_TO_MEDIUMP) != 0)
+    if (compileOptions.forceShaderPrecisionHighpToMediump)
     {
         if (!ForceShaderPrecisionToMediump(root, &mSymbolTable, mShaderType))
         {
@@ -998,14 +996,14 @@
                          mExtensionBehavior, mResources, mTessControlShaderOutputVertices);
         collectInterfaceBlocks();
         mVariablesCollected = true;
-        if ((compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS) != 0)
+        if (compileOptions.useUnusedStandardSharedBlocks)
         {
             if (!useAllMembersInUnusedStandardAndSharedBlocks(root))
             {
                 return false;
             }
         }
-        if ((compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) != 0)
+        if (compileOptions.enforcePackingRestrictions)
         {
             int maxUniformVectors = GetMaxUniformVectorsForShaderType(mShaderType, mResources);
             // Returns true if, after applying the packing rules in the GLSL ES 1.00.17 spec
@@ -1017,10 +1015,9 @@
             }
         }
         bool needInitializeOutputVariables =
-            (compileOptions & SH_INIT_OUTPUT_VARIABLES) != 0 && mShaderType != GL_COMPUTE_SHADER;
+            compileOptions.initOutputVariables && mShaderType != GL_COMPUTE_SHADER;
         needInitializeOutputVariables |=
-            (compileOptions & SH_INIT_FRAGMENT_OUTPUT_VARIABLES) != 0 &&
-            mShaderType == GL_FRAGMENT_SHADER;
+            compileOptions.initFragmentOutputVariables && mShaderType == GL_FRAGMENT_SHADER;
         if (needInitializeOutputVariables)
         {
             if (!initializeOutputVariables(root))
@@ -1044,8 +1041,7 @@
     // It may have been already initialized among other output variables, in that case we don't
     // need to initialize it twice.
     if (mShaderType == GL_VERTEX_SHADER && !mGLPositionInitialized &&
-        ((compileOptions & SH_INIT_GL_POSITION) != 0 ||
-         mOutputType == SH_GLSL_COMPATIBILITY_OUTPUT))
+        (compileOptions.initGLPosition || mOutputType == SH_GLSL_COMPATIBILITY_OUTPUT))
     {
         if (!initializeGLPosition(root))
         {
@@ -1097,7 +1093,7 @@
         }
     }
 
-    if (getShaderType() == GL_VERTEX_SHADER && (compileOptions & SH_CLAMP_POINT_SIZE) != 0)
+    if (getShaderType() == GL_VERTEX_SHADER && compileOptions.clampPointSize)
     {
         if (!ClampPointSize(this, root, mResources.MaxPointSize, &getSymbolTable()))
         {
@@ -1105,7 +1101,7 @@
         }
     }
 
-    if (getShaderType() == GL_FRAGMENT_SHADER && (compileOptions & SH_CLAMP_FRAG_DEPTH) != 0)
+    if (getShaderType() == GL_FRAGMENT_SHADER && compileOptions.clampFragDepth)
     {
         if (!ClampFragDepth(this, root, &getSymbolTable()))
         {
@@ -1113,7 +1109,7 @@
         }
     }
 
-    if ((compileOptions & SH_REWRITE_REPEATED_ASSIGN_TO_SWIZZLED) != 0)
+    if (compileOptions.rewriteRepeatedAssignToSwizzled)
     {
         if (!sh::RewriteRepeatedAssignToSwizzled(this, root))
         {
@@ -1121,7 +1117,7 @@
         }
     }
 
-    if ((compileOptions & SH_REMOVE_DYNAMIC_INDEXING_OF_SWIZZLED_VECTOR) != 0)
+    if (compileOptions.removeDynamicIndexingOfSwizzledVector)
     {
         if (!sh::RemoveDynamicIndexingOfSwizzledVector(this, root, &getSymbolTable(), nullptr))
         {
@@ -1157,7 +1153,7 @@
 
 bool TCompiler::compile(const char *const shaderStrings[],
                         size_t numStrings,
-                        ShCompileOptions compileOptionsIn)
+                        const ShCompileOptions &compileOptionsIn)
 {
 #if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
     DumpFuzzerCase(shaderStrings, numStrings, mShaderType, mShaderSpec, mOutputType,
@@ -1173,7 +1169,7 @@
     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;
+        compileOptions.flattenPragmaSTDGLInvariantAll = true;
     }
 
     TScopedPoolAllocator scopedAlloc(&allocator);
@@ -1181,12 +1177,12 @@
 
     if (root)
     {
-        if ((compileOptions & SH_INTERMEDIATE_TREE) != 0)
+        if (compileOptions.intermediateTree)
         {
             OutputTree(root, mInfoSink.info);
         }
 
-        if ((compileOptions & SH_OBJECT_CODE) != 0)
+        if (compileOptions.objectCode)
         {
             PerformanceDiagnostics perfDiagnostics(&mDiagnostics);
             if (!translate(root, compileOptions, &perfDiagnostics))
@@ -1199,11 +1195,11 @@
         {
             bool lookForDrawID =
                 IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_multi_draw) &&
-                (compileOptions & SH_EMULATE_GL_DRAW_ID) != 0;
+                compileOptions.emulateGLDrawID;
             bool lookForBaseVertexBaseInstance =
                 IsExtensionEnabled(mExtensionBehavior,
                                    TExtension::ANGLE_base_vertex_base_instance_shader_builtin) &&
-                (compileOptions & SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE) != 0;
+                compileOptions.emulateGLBaseVertexBaseInstance;
 
             if (lookForDrawID || lookForBaseVertexBaseInstance)
             {
@@ -1597,9 +1593,9 @@
     return true;
 }
 
-bool TCompiler::shouldCollectVariables(ShCompileOptions compileOptions)
+bool TCompiler::shouldCollectVariables(const ShCompileOptions &compileOptions)
 {
-    return (compileOptions & SH_VARIABLES) != 0;
+    return compileOptions.variables;
 }
 
 bool TCompiler::wereVariablesCollected() const
diff --git a/src/compiler/translator/Compiler.h b/src/compiler/translator/Compiler.h
index dfb0473..cbc52d8 100644
--- a/src/compiler/translator/Compiler.h
+++ b/src/compiler/translator/Compiler.h
@@ -54,7 +54,7 @@
 bool RemoveInvariant(sh::GLenum shaderType,
                      int shaderVersion,
                      ShShaderOutput outputType,
-                     ShCompileOptions compileOptions);
+                     const ShCompileOptions &compileOptions);
 
 //
 // The base class used to back handles returned to the driver.
@@ -101,11 +101,11 @@
     // allocator. Returns nullptr whenever there are compilation errors.
     TIntermBlock *compileTreeForTesting(const char *const shaderStrings[],
                                         size_t numStrings,
-                                        ShCompileOptions compileOptions);
+                                        const ShCompileOptions &compileOptions);
 
     bool compile(const char *const shaderStrings[],
                  size_t numStrings,
-                 ShCompileOptions compileOptions);
+                 const ShCompileOptions &compileOptions);
 
     // Get results of the last compilation.
     int getShaderVersion() const { return mShaderVersion; }
@@ -143,7 +143,7 @@
 
     bool isHighPrecisionSupported() const;
 
-    bool shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const;
+    bool shouldRunLoopAndIndexingValidation(const ShCompileOptions &compileOptions) const;
     bool shouldLimitTypeSizes() const;
 
     // Get the resources set by InitBuiltInSymbolTable
@@ -205,11 +205,11 @@
   protected:
     // Add emulated functions to the built-in function emulator.
     virtual void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
-                                             ShCompileOptions compileOptions)
+                                             const ShCompileOptions &compileOptions)
     {}
     // Translate to object code. May generate performance warnings through the diagnostics.
     [[nodiscard]] virtual bool translate(TIntermBlock *root,
-                                         ShCompileOptions compileOptions,
+                                         const ShCompileOptions &compileOptions,
                                          PerformanceDiagnostics *perfDiagnostics) = 0;
     // Get built-in extensions with default behavior.
     const TExtensionBehavior &getExtensionBehavior() const;
@@ -220,7 +220,7 @@
     const BuiltInFunctionEmulator &getBuiltInFunctionEmulator() const;
 
     virtual bool shouldFlattenPragmaStdglInvariantAll() = 0;
-    virtual bool shouldCollectVariables(ShCompileOptions compileOptions);
+    virtual bool shouldCollectVariables(const ShCompileOptions &compileOptions);
 
     bool wereVariablesCollected() const;
     std::vector<sh::ShaderVariable> mAttributes;
@@ -277,7 +277,7 @@
 
     TIntermBlock *compileTreeImpl(const char *const shaderStrings[],
                                   size_t numStrings,
-                                  const ShCompileOptions compileOptions);
+                                  const ShCompileOptions &compileOptions);
 
     // Fetches and stores shader metadata that is not stored within the AST itself, such as shader
     // version.
@@ -289,7 +289,7 @@
     // Does checks that need to be run after parsing is complete and returns true if they pass.
     bool checkAndSimplifyAST(TIntermBlock *root,
                              const TParseContext &parseContext,
-                             ShCompileOptions compileOptions);
+                             const ShCompileOptions &compileOptions);
 
     bool postParseChecks(const TParseContext &parseContext);
 
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index 001d90f..287765d 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -197,7 +197,7 @@
 
 void ResetExtensionBehavior(const ShBuiltInResources &resources,
                             TExtensionBehavior &extBehavior,
-                            const ShCompileOptions compileOptions)
+                            const ShCompileOptions &compileOptions)
 {
     for (auto &ext : extBehavior)
     {
@@ -205,7 +205,7 @@
     }
     if (resources.ARB_texture_rectangle)
     {
-        if ((compileOptions & SH_DISABLE_ARB_TEXTURE_RECTANGLE) != 0)
+        if (compileOptions.disableARBTextureRectangle)
         {
             // Remove ARB_texture_rectangle so it can't be enabled by extension directives.
             extBehavior.erase(TExtension::ARB_texture_rectangle);
diff --git a/src/compiler/translator/Initialize.h b/src/compiler/translator/Initialize.h
index 9e7b2e2..727e44e 100644
--- a/src/compiler/translator/Initialize.h
+++ b/src/compiler/translator/Initialize.h
@@ -23,7 +23,7 @@
 // extensions will remain unsupported.
 void ResetExtensionBehavior(const ShBuiltInResources &resources,
                             TExtensionBehavior &extensionBehavior,
-                            const ShCompileOptions compileOptions);
+                            const ShCompileOptions &compileOptions);
 
 }  // namespace sh
 
diff --git a/src/compiler/translator/OutputESSL.cpp b/src/compiler/translator/OutputESSL.cpp
index e3924b2..95c7e1e 100644
--- a/src/compiler/translator/OutputESSL.cpp
+++ b/src/compiler/translator/OutputESSL.cpp
@@ -11,7 +11,7 @@
 
 TOutputESSL::TOutputESSL(TCompiler *compiler,
                          TInfoSinkBase &objSink,
-                         ShCompileOptions compileOptions)
+                         const ShCompileOptions &compileOptions)
     : TOutputGLSLBase(compiler, objSink, compileOptions)
 {}
 
@@ -36,7 +36,7 @@
     // Check WEBGL_video_texture invocation first.
     if (name == "textureVideoWEBGL")
     {
-        if (option & SH_TAKE_VIDEO_TEXTURE_AS_EXTERNAL_OES)
+        if (option.takeVideoTextureAsExternalOES)
         {
             // TODO(http://anglebug.com/3889): Implement external image situation.
             UNIMPLEMENTED();
diff --git a/src/compiler/translator/OutputESSL.h b/src/compiler/translator/OutputESSL.h
index 948536f..9d14de2 100644
--- a/src/compiler/translator/OutputESSL.h
+++ b/src/compiler/translator/OutputESSL.h
@@ -15,7 +15,9 @@
 class TOutputESSL : public TOutputGLSLBase
 {
   public:
-    TOutputESSL(TCompiler *compiler, TInfoSinkBase &objSink, ShCompileOptions compileOptions);
+    TOutputESSL(TCompiler *compiler,
+                TInfoSinkBase &objSink,
+                const ShCompileOptions &compileOptions);
 
   protected:
     bool writeVariablePrecision(TPrecision precision) override;
diff --git a/src/compiler/translator/OutputGLSL.cpp b/src/compiler/translator/OutputGLSL.cpp
index 4484008..0045a76 100644
--- a/src/compiler/translator/OutputGLSL.cpp
+++ b/src/compiler/translator/OutputGLSL.cpp
@@ -13,7 +13,7 @@
 
 TOutputGLSL::TOutputGLSL(TCompiler *compiler,
                          TInfoSinkBase &objSink,
-                         ShCompileOptions compileOptions)
+                         const ShCompileOptions &compileOptions)
     : TOutputGLSLBase(compiler, objSink, compileOptions)
 {}
 
@@ -67,7 +67,7 @@
     // Check WEBGL_video_texture invocation first.
     if (name == "textureVideoWEBGL")
     {
-        if (option & SH_TAKE_VIDEO_TEXTURE_AS_EXTERNAL_OES)
+        if (option.takeVideoTextureAsExternalOES)
         {
             // TODO(http://anglebug.com/3889): Implement external image situation.
             UNIMPLEMENTED();
@@ -81,19 +81,19 @@
     }
 
     static const char *simpleRename[]       = {"texture2DLodEXT",
-                                         "texture2DLod",
-                                         "texture2DProjLodEXT",
-                                         "texture2DProjLod",
-                                         "textureCubeLodEXT",
-                                         "textureCubeLod",
-                                         "texture2DGradEXT",
-                                         "texture2DGradARB",
-                                         "texture2DProjGradEXT",
-                                         "texture2DProjGradARB",
-                                         "textureCubeGradEXT",
-                                         "textureCubeGradARB",
-                                         nullptr,
-                                         nullptr};
+                                               "texture2DLod",
+                                               "texture2DProjLodEXT",
+                                               "texture2DProjLod",
+                                               "textureCubeLodEXT",
+                                               "textureCubeLod",
+                                               "texture2DGradEXT",
+                                               "texture2DGradARB",
+                                               "texture2DProjGradEXT",
+                                               "texture2DProjGradARB",
+                                               "textureCubeGradEXT",
+                                               "textureCubeGradARB",
+                                               nullptr,
+                                               nullptr};
     static const char *legacyToCoreRename[] = {
         "texture2D", "texture", "texture2DProj", "textureProj", "texture2DLod", "textureLod",
         "texture2DProjLod", "textureProjLod", "texture2DRect", "texture", "texture2DRectProj",
diff --git a/src/compiler/translator/OutputGLSL.h b/src/compiler/translator/OutputGLSL.h
index 1a77efd..ff55e16 100644
--- a/src/compiler/translator/OutputGLSL.h
+++ b/src/compiler/translator/OutputGLSL.h
@@ -15,7 +15,9 @@
 class TOutputGLSL : public TOutputGLSLBase
 {
   public:
-    TOutputGLSL(TCompiler *compiler, TInfoSinkBase &objSink, ShCompileOptions compileOptions);
+    TOutputGLSL(TCompiler *compiler,
+                TInfoSinkBase &objSink,
+                const ShCompileOptions &compileOptions);
 
   protected:
     bool writeVariablePrecision(TPrecision) override;
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index 6b38824..57735a5 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -83,7 +83,7 @@
 
 TOutputGLSLBase::TOutputGLSLBase(TCompiler *compiler,
                                  TInfoSinkBase &objSink,
-                                 ShCompileOptions compileOptions)
+                                 const ShCompileOptions &compileOptions)
     : TIntermTraverser(true, true, true, &compiler->getSymbolTable()),
       mObjSink(objSink),
       mDeclaringVariable(false),
@@ -330,7 +330,7 @@
 const char *TOutputGLSLBase::mapQualifierToString(TQualifier qualifier)
 {
     if (sh::IsGLSL410OrOlder(mOutput) && mShaderVersion >= 300 &&
-        (mCompileOptions & SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3) != 0)
+        mCompileOptions.removeInvariantAndCentroidForESSL3)
     {
         switch (qualifier)
         {
@@ -1295,9 +1295,9 @@
     out << "}";
 }
 
-void WritePragma(TInfoSinkBase &out, ShCompileOptions compileOptions, const TPragma &pragma)
+void WritePragma(TInfoSinkBase &out, const ShCompileOptions &compileOptions, const TPragma &pragma)
 {
-    if ((compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) == 0)
+    if (!compileOptions.flattenPragmaSTDGLInvariantAll)
     {
         if (pragma.stdgl.invariantAll)
             out << "#pragma STDGL invariant(all)\n";
@@ -1453,12 +1453,12 @@
         return;
 
     const bool isVertexShader = (compiler.getShaderType() == GL_VERTEX_SHADER);
-    if ((compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) != 0)
+    if (compileOptions.initializeBuiltinsForInstancedMultiview)
     {
         // Emit ARB_shader_viewport_layer_array/NV_viewport_array2 in a vertex shader if the
         // SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set and the
         // OVR_multiview(2) extension is requested.
-        if (isVertexShader && (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0)
+        if (isVertexShader && compileOptions.selectViewInNvGLSLVertexShader)
         {
             sink << "#if defined(GL_ARB_shader_viewport_layer_array)\n"
                  << "#extension GL_ARB_shader_viewport_layer_array : require\n"
diff --git a/src/compiler/translator/OutputGLSLBase.h b/src/compiler/translator/OutputGLSLBase.h
index df978bb..d585cd8 100644
--- a/src/compiler/translator/OutputGLSLBase.h
+++ b/src/compiler/translator/OutputGLSLBase.h
@@ -22,7 +22,9 @@
 class TOutputGLSLBase : public TIntermTraverser
 {
   public:
-    TOutputGLSLBase(TCompiler *compiler, TInfoSinkBase &objSink, ShCompileOptions compileOptions);
+    TOutputGLSLBase(TCompiler *compiler,
+                    TInfoSinkBase &objSink,
+                    const ShCompileOptions &compileOptions);
 
     ShShaderOutput getShaderOutput() const { return mOutput; }
 
@@ -112,10 +114,10 @@
 
     bool mHighPrecisionSupported;
 
-    ShCompileOptions mCompileOptions;
+    const ShCompileOptions &mCompileOptions;
 };
 
-void WritePragma(TInfoSinkBase &out, ShCompileOptions compileOptions, const TPragma &pragma);
+void WritePragma(TInfoSinkBase &out, const ShCompileOptions &compileOptions, const TPragma &pragma);
 
 void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out,
                                          sh::TLayoutPrimitiveType inputPrimitive,
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 121a47a..331d50e 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -306,7 +306,7 @@
                        int numRenderTargets,
                        int maxDualSourceDrawBuffers,
                        const std::vector<ShaderVariable> &uniforms,
-                       ShCompileOptions compileOptions,
+                       const ShCompileOptions &compileOptions,
                        sh::WorkGroupSize workGroupSize,
                        TSymbolTable *symbolTable,
                        PerformanceDiagnostics *perfDiagnostics,
@@ -369,10 +369,9 @@
     mTextureFunctionHLSL = new TextureFunctionHLSL;
     mImageFunctionHLSL   = new ImageFunctionHLSL;
     mAtomicCounterFunctionHLSL =
-        new AtomicCounterFunctionHLSL((compileOptions & SH_FORCE_ATOMIC_VALUE_RESOLUTION) != 0);
+        new AtomicCounterFunctionHLSL(compileOptions.forceAtomicValueResolution);
 
-    unsigned int firstUniformRegister =
-        (compileOptions & SH_SKIP_D3D_CONSTANT_REGISTER_ZERO) != 0 ? 1u : 0u;
+    unsigned int firstUniformRegister = compileOptions.skipD3DConstantRegisterZero ? 1u : 0u;
     mResourcesHLSL = new ResourcesHLSL(mStructureHLSL, outputType, uniforms, firstUniformRegister);
 
     if (mOutputType == SH_HLSL_3_0_OUTPUT)
@@ -412,7 +411,7 @@
 {
     BuiltInFunctionEmulator builtInFunctionEmulator;
     InitBuiltInFunctionEmulatorForHLSL(&builtInFunctionEmulator);
-    if ((mCompileOptions & SH_EMULATE_ISNAN_FLOAT_FUNCTION) != 0)
+    if (mCompileOptions.emulateIsnanFloatFunction)
     {
         InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(&builtInFunctionEmulator,
                                                            mShaderVersion);
@@ -1092,8 +1091,7 @@
         out << "\n";
     }
 
-    bool getDimensionsIgnoresBaseLevel =
-        (mCompileOptions & SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL) != 0;
+    bool getDimensionsIgnoresBaseLevel = mCompileOptions.HLSLGetDimensionsIgnoresBaseLevel;
     mTextureFunctionHLSL->textureFunctionHeader(out, mOutputType, getDimensionsIgnoresBaseLevel);
     mImageFunctionHLSL->imageFunctionHeader(out);
     mAtomicCounterFunctionHLSL->atomicCounterFunctionHeader(out);
@@ -3220,7 +3218,7 @@
 
 void OutputHLSL::outputLineDirective(TInfoSinkBase &out, int line)
 {
-    if ((mCompileOptions & SH_LINE_DIRECTIVES) != 0 && line > 0)
+    if (mCompileOptions.lineDirectives && line > 0)
     {
         out << "\n";
         out << "#line " << line;
diff --git a/src/compiler/translator/OutputHLSL.h b/src/compiler/translator/OutputHLSL.h
index 66bd33e..0a602f9 100644
--- a/src/compiler/translator/OutputHLSL.h
+++ b/src/compiler/translator/OutputHLSL.h
@@ -46,7 +46,7 @@
                int numRenderTargets,
                int maxDualSourceDrawBuffers,
                const std::vector<ShaderVariable> &uniforms,
-               ShCompileOptions compileOptions,
+               const ShCompileOptions &compileOptions,
                sh::WorkGroupSize workGroupSize,
                TSymbolTable *symbolTable,
                PerformanceDiagnostics *perfDiagnostics,
@@ -160,7 +160,7 @@
     const TExtensionBehavior &mExtensionBehavior;
     const char *mSourcePath;
     const ShShaderOutput mOutputType;
-    ShCompileOptions mCompileOptions;
+    const ShCompileOptions &mCompileOptions;
 
     bool mInsideFunction;
     bool mInsideMain;
diff --git a/src/compiler/translator/OutputSPIRV.cpp b/src/compiler/translator/OutputSPIRV.cpp
index 54535b4..40f3eba 100644
--- a/src/compiler/translator/OutputSPIRV.cpp
+++ b/src/compiler/translator/OutputSPIRV.cpp
@@ -179,7 +179,7 @@
 class OutputSPIRVTraverser : public TIntermTraverser
 {
   public:
-    OutputSPIRVTraverser(TCompiler *compiler, ShCompileOptions compileOptions);
+    OutputSPIRVTraverser(TCompiler *compiler, const ShCompileOptions &compileOptions);
     ~OutputSPIRVTraverser() override;
 
     spirv::Blob getSpirv();
@@ -362,7 +362,7 @@
                                                uint32_t fieldIndex);
 
     TCompiler *mCompiler;
-    [[maybe_unused]] ShCompileOptions mCompileOptions;
+    [[maybe_unused]] const ShCompileOptions &mCompileOptions;
 
     SPIRVBuilder mBuilder;
 
@@ -494,7 +494,8 @@
     }
 }
 
-OutputSPIRVTraverser::OutputSPIRVTraverser(TCompiler *compiler, ShCompileOptions compileOptions)
+OutputSPIRVTraverser::OutputSPIRVTraverser(TCompiler *compiler,
+                                           const ShCompileOptions &compileOptions)
     : TIntermTraverser(true, true, true, &compiler->getSymbolTable()),
       mCompiler(compiler),
       mCompileOptions(compileOptions),
@@ -6352,7 +6353,7 @@
 }
 }  // anonymous namespace
 
-bool OutputSPIRV(TCompiler *compiler, TIntermBlock *root, ShCompileOptions compileOptions)
+bool OutputSPIRV(TCompiler *compiler, TIntermBlock *root, const ShCompileOptions &compileOptions)
 {
     // Find the list of nodes that require NoContraction (as a result of |precise|).
     if (compiler->hasAnyPreciseType())
diff --git a/src/compiler/translator/OutputSPIRV.h b/src/compiler/translator/OutputSPIRV.h
index 99c9dd0..2ce40dd 100644
--- a/src/compiler/translator/OutputSPIRV.h
+++ b/src/compiler/translator/OutputSPIRV.h
@@ -13,7 +13,7 @@
 
 namespace sh
 {
-bool OutputSPIRV(TCompiler *compiler, TIntermBlock *root, ShCompileOptions compileOptions);
+bool OutputSPIRV(TCompiler *compiler, TIntermBlock *root, const ShCompileOptions &compileOptions);
 }  // namespace sh
 
 #endif  // COMPILER_TRANSLATOR_OUTPUTSPIRV_H_
diff --git a/src/compiler/translator/OutputVulkanGLSL.cpp b/src/compiler/translator/OutputVulkanGLSL.cpp
index 69f2b0c..a08e446 100644
--- a/src/compiler/translator/OutputVulkanGLSL.cpp
+++ b/src/compiler/translator/OutputVulkanGLSL.cpp
@@ -22,7 +22,7 @@
 TOutputVulkanGLSL::TOutputVulkanGLSL(TCompiler *compiler,
                                      TInfoSinkBase &objSink,
                                      bool enablePrecision,
-                                     ShCompileOptions compileOptions)
+                                     const ShCompileOptions &compileOptions)
     : TOutputGLSL(compiler, objSink, compileOptions),
       mNextUnusedBinding(0),
       mNextUnusedInputLocation(0),
diff --git a/src/compiler/translator/OutputVulkanGLSL.h b/src/compiler/translator/OutputVulkanGLSL.h
index 2d5dd5b..ef88f90 100644
--- a/src/compiler/translator/OutputVulkanGLSL.h
+++ b/src/compiler/translator/OutputVulkanGLSL.h
@@ -23,7 +23,7 @@
     TOutputVulkanGLSL(TCompiler *compiler,
                       TInfoSinkBase &objSink,
                       bool enablePrecision,
-                      ShCompileOptions compileOptions);
+                      const ShCompileOptions &compileOptions);
 
     uint32_t nextUnusedBinding() { return mNextUnusedBinding++; }
     uint32_t nextUnusedInputLocation(uint32_t consumedCount)
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 5e43aae..36f768f 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -190,7 +190,7 @@
                              TExtensionBehavior &ext,
                              sh::GLenum type,
                              ShShaderSpec spec,
-                             ShCompileOptions options,
+                             const ShCompileOptions &options,
                              bool checksPrecErrors,
                              TDiagnostics *diagnostics,
                              const ShBuiltInResources &resources,
@@ -3069,7 +3069,7 @@
     const ImmutableString &identifier)
 {
     TType *type = new TType(publicType);
-    if ((mCompileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) != 0 &&
+    if (mCompileOptions.flattenPragmaSTDGLInvariantAll &&
         mDirectiveHandler.pragma().stdgl.invariantAll)
     {
         TQualifier qualifier = type->getQualifier();
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 5cfb402..b98db5f 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -37,7 +37,7 @@
                   TExtensionBehavior &ext,
                   sh::GLenum type,
                   ShShaderSpec spec,
-                  ShCompileOptions options,
+                  const ShCompileOptions &options,
                   bool checksPrecErrors,
                   TDiagnostics *diagnostics,
                   const ShBuiltInResources &resources,
diff --git a/src/compiler/translator/ShaderLang.cpp b/src/compiler/translator/ShaderLang.cpp
index 3964480..3e3fa9b 100644
--- a/src/compiler/translator/ShaderLang.cpp
+++ b/src/compiler/translator/ShaderLang.cpp
@@ -402,7 +402,7 @@
 bool Compile(const ShHandle handle,
              const char *const shaderStrings[],
              size_t numStrings,
-             ShCompileOptions compileOptions)
+             const ShCompileOptions &compileOptions)
 {
     TCompiler *compiler = GetCompilerFromHandle(handle);
     ASSERT(compiler);
diff --git a/src/compiler/translator/SymbolTable.h b/src/compiler/translator/SymbolTable.h
index 75302a4..fb05729 100644
--- a/src/compiler/translator/SymbolTable.h
+++ b/src/compiler/translator/SymbolTable.h
@@ -308,12 +308,6 @@
                             const ShBuiltInResources &resources);
     void clearCompilationResults();
 
-    int getDefaultUniformsBindingIndex() const { return mResources.DefaultUniformsBindingIndex; }
-    int getDriverUniformsBindingIndex() const { return mResources.DriverUniformsBindingIndex; }
-    int getUBOArgumentBufferBindingIndex() const
-    {
-        return mResources.UBOArgumentBufferBindingIndex;
-    }
     ShShaderSpec getShaderSpec() const { return mShaderSpec; }
 
   private:
diff --git a/src/compiler/translator/TranslatorESSL.cpp b/src/compiler/translator/TranslatorESSL.cpp
index f77938e..2246dc0 100644
--- a/src/compiler/translator/TranslatorESSL.cpp
+++ b/src/compiler/translator/TranslatorESSL.cpp
@@ -19,16 +19,16 @@
 {}
 
 void TranslatorESSL::initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
-                                                 ShCompileOptions compileOptions)
+                                                 const ShCompileOptions &compileOptions)
 {
-    if ((compileOptions & SH_EMULATE_ATAN2_FLOAT_FUNCTION) != 0)
+    if (compileOptions.emulateAtan2FloatFunction)
     {
         InitBuiltInAtanFunctionEmulatorForGLSLWorkarounds(emu);
     }
 }
 
 bool TranslatorESSL::translate(TIntermBlock *root,
-                               ShCompileOptions compileOptions,
+                               const ShCompileOptions &compileOptions,
                                PerformanceDiagnostics * /*perfDiagnostics*/)
 {
     TInfoSinkBase &sink = getInfoSink().obj;
@@ -111,7 +111,7 @@
     return true;
 }
 
-void TranslatorESSL::writeExtensionBehavior(ShCompileOptions compileOptions)
+void TranslatorESSL::writeExtensionBehavior(const ShCompileOptions &compileOptions)
 {
     TInfoSinkBase &sink                   = getInfoSink().obj;
     const TExtensionBehavior &extBehavior = getExtensionBehavior();
@@ -162,13 +162,13 @@
             else if (iter->first == TExtension::ANGLE_multi_draw)
             {
                 // Don't emit anything. This extension is emulated
-                ASSERT((compileOptions & SH_EMULATE_GL_DRAW_ID) != 0);
+                ASSERT(compileOptions.emulateGLDrawID);
                 continue;
             }
             else if (iter->first == TExtension::ANGLE_base_vertex_base_instance_shader_builtin)
             {
                 // Don't emit anything. This extension is emulated
-                ASSERT((compileOptions & SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE) != 0);
+                ASSERT(compileOptions.emulateGLBaseVertexBaseInstance);
                 continue;
             }
             else if (iter->first == TExtension::ANGLE_shader_pixel_local_storage)
diff --git a/src/compiler/translator/TranslatorESSL.h b/src/compiler/translator/TranslatorESSL.h
index 6b438d2..913d441 100644
--- a/src/compiler/translator/TranslatorESSL.h
+++ b/src/compiler/translator/TranslatorESSL.h
@@ -19,15 +19,15 @@
 
   protected:
     void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
-                                     ShCompileOptions compileOptions) override;
+                                     const ShCompileOptions &compileOptions) override;
 
     [[nodiscard]] bool translate(TIntermBlock *root,
-                                 ShCompileOptions compileOptions,
+                                 const ShCompileOptions &compileOptions,
                                  PerformanceDiagnostics *perfDiagnostics) override;
     bool shouldFlattenPragmaStdglInvariantAll() override;
 
   private:
-    void writeExtensionBehavior(ShCompileOptions compileOptions);
+    void writeExtensionBehavior(const ShCompileOptions &compileOptions);
 };
 
 }  // namespace sh
diff --git a/src/compiler/translator/TranslatorGLSL.cpp b/src/compiler/translator/TranslatorGLSL.cpp
index 475924a..76e0130 100644
--- a/src/compiler/translator/TranslatorGLSL.cpp
+++ b/src/compiler/translator/TranslatorGLSL.cpp
@@ -23,19 +23,19 @@
 {}
 
 void TranslatorGLSL::initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
-                                                 ShCompileOptions compileOptions)
+                                                 const ShCompileOptions &compileOptions)
 {
-    if ((compileOptions & SH_EMULATE_ABS_INT_FUNCTION) != 0)
+    if (compileOptions.emulateAbsIntFunction)
     {
         InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(emu, getShaderType());
     }
 
-    if ((compileOptions & SH_EMULATE_ISNAN_FLOAT_FUNCTION) != 0)
+    if (compileOptions.emulateIsnanFloatFunction)
     {
         InitBuiltInIsnanFunctionEmulatorForGLSLWorkarounds(emu, getShaderVersion());
     }
 
-    if ((compileOptions & SH_EMULATE_ATAN2_FLOAT_FUNCTION) != 0)
+    if (compileOptions.emulateAtan2FloatFunction)
     {
         InitBuiltInAtanFunctionEmulatorForGLSLWorkarounds(emu);
     }
@@ -45,7 +45,7 @@
 }
 
 bool TranslatorGLSL::translate(TIntermBlock *root,
-                               ShCompileOptions compileOptions,
+                               const ShCompileOptions &compileOptions,
                                PerformanceDiagnostics * /*perfDiagnostics*/)
 {
     TInfoSinkBase &sink = getInfoSink().obj;
@@ -64,8 +64,7 @@
     // variables. It should be harmless to do this twice in the case that the shader also explicitly
     // did this. However, it's important to emit invariant qualifiers only for those built-in
     // variables that are actually used, to avoid affecting the behavior of the shader.
-    if ((compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) != 0 &&
-        getPragma().stdgl.invariantAll &&
+    if (compileOptions.flattenPragmaSTDGLInvariantAll && getPragma().stdgl.invariantAll &&
         !sh::RemoveInvariant(getShaderType(), getShaderVersion(), getOutputType(), compileOptions))
     {
         ASSERT(wereVariablesCollected());
@@ -93,7 +92,7 @@
         }
     }
 
-    if ((compileOptions & SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH) != 0)
+    if (compileOptions.rewriteTexelFetchOffsetToTexelFetch)
     {
         if (!sh::RewriteTexelFetchOffset(this, root, getSymbolTable(), getShaderVersion()))
         {
@@ -101,7 +100,7 @@
         }
     }
 
-    if ((compileOptions & SH_REWRITE_FLOAT_UNARY_MINUS_OPERATOR) != 0)
+    if (compileOptions.rewriteFloatUnaryMinusOperator)
     {
         if (!sh::RewriteUnaryMinusOperatorFloat(this, root))
         {
@@ -109,7 +108,7 @@
         }
     }
 
-    if ((compileOptions & SH_REWRITE_ROW_MAJOR_MATRICES) != 0 && getShaderVersion() >= 300)
+    if (compileOptions.rewriteRowMajorMatrices && getShaderVersion() >= 300)
     {
         if (!RewriteRowMajorMatrices(this, root, &getSymbolTable()))
         {
@@ -223,9 +222,9 @@
     return IsGLSL130OrNewer(getOutputType());
 }
 
-bool TranslatorGLSL::shouldCollectVariables(ShCompileOptions compileOptions)
+bool TranslatorGLSL::shouldCollectVariables(const ShCompileOptions &compileOptions)
 {
-    return (compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) != 0 ||
+    return compileOptions.flattenPragmaSTDGLInvariantAll ||
            TCompiler::shouldCollectVariables(compileOptions);
 }
 
@@ -243,7 +242,8 @@
     }
 }
 
-void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root, ShCompileOptions compileOptions)
+void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root,
+                                            const ShCompileOptions &compileOptions)
 {
     bool usesTextureCubeMapArray = false;
     bool usesTextureBuffer       = false;
diff --git a/src/compiler/translator/TranslatorGLSL.h b/src/compiler/translator/TranslatorGLSL.h
index b75a8ef..0e0c65f 100644
--- a/src/compiler/translator/TranslatorGLSL.h
+++ b/src/compiler/translator/TranslatorGLSL.h
@@ -19,17 +19,17 @@
 
   protected:
     void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
-                                     ShCompileOptions compileOptions) override;
+                                     const ShCompileOptions &compileOptions) override;
 
     [[nodiscard]] bool translate(TIntermBlock *root,
-                                 ShCompileOptions compileOptions,
+                                 const ShCompileOptions &compileOptions,
                                  PerformanceDiagnostics *perfDiagnostics) override;
     bool shouldFlattenPragmaStdglInvariantAll() override;
-    bool shouldCollectVariables(ShCompileOptions compileOptions) override;
+    bool shouldCollectVariables(const ShCompileOptions &compileOptions) override;
 
   private:
     void writeVersion(TIntermNode *root);
-    void writeExtensionBehavior(TIntermNode *root, ShCompileOptions compileOptions);
+    void writeExtensionBehavior(TIntermNode *root, const ShCompileOptions &compileOptions);
     void conditionallyOutputInvariantDeclaration(const char *builtinVaryingName);
 };
 
diff --git a/src/compiler/translator/TranslatorHLSL.cpp b/src/compiler/translator/TranslatorHLSL.cpp
index 68d4639..c0c8afe 100644
--- a/src/compiler/translator/TranslatorHLSL.cpp
+++ b/src/compiler/translator/TranslatorHLSL.cpp
@@ -37,7 +37,7 @@
 {}
 
 bool TranslatorHLSL::translate(TIntermBlock *root,
-                               ShCompileOptions compileOptions,
+                               const ShCompileOptions &compileOptions,
                                PerformanceDiagnostics *perfDiagnostics)
 {
     // A few transformations leave the tree in an inconsistent state.  For example, when unfolding
@@ -174,7 +174,7 @@
         return false;
     }
 
-    if ((compileOptions & SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS) != 0)
+    if (compileOptions.expandSelectHLSLIntegerPowExpressions)
     {
         if (!sh::ExpandIntegerPowExpressions(this, root, &getSymbolTable()))
         {
@@ -182,7 +182,7 @@
         }
     }
 
-    if ((compileOptions & SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH) != 0)
+    if (compileOptions.rewriteTexelFetchOffsetToTexelFetch)
     {
         if (!sh::RewriteTexelFetchOffset(this, root, getSymbolTable(), getShaderVersion()))
         {
@@ -190,8 +190,7 @@
         }
     }
 
-    if (((compileOptions & SH_REWRITE_INTEGER_UNARY_MINUS_OPERATOR) != 0) &&
-        getShaderType() == GL_VERTEX_SHADER)
+    if (compileOptions.rewriteIntegerUnaryMinusOperator && getShaderType() == GL_VERTEX_SHADER)
     {
         if (!sh::RewriteUnaryMinusOperatorInt(this, root))
         {
@@ -219,8 +218,7 @@
     // In order to get the exact maximum of slots are available for shader resources, which would
     // been bound with StructuredBuffer, we only translate uniform block with a large array member
     // into StructuredBuffer when shader version is 300.
-    if (getShaderVersion() == 300 &&
-        (compileOptions & SH_ALLOW_TRANSLATE_UNIFORM_BLOCK_TO_STRUCTUREDBUFFER) != 0)
+    if (getShaderVersion() == 300 && compileOptions.allowTranslateUniformBlockToStructuredBuffer)
     {
         if (!sh::RecordUniformBlocksWithLargeArrayMember(root, mUniformBlockOptimizedMap,
                                                          mSlowCompilingUniformBlockSet))
diff --git a/src/compiler/translator/TranslatorHLSL.h b/src/compiler/translator/TranslatorHLSL.h
index 78f5191..309176b 100644
--- a/src/compiler/translator/TranslatorHLSL.h
+++ b/src/compiler/translator/TranslatorHLSL.h
@@ -33,12 +33,12 @@
 
   protected:
     [[nodiscard]] bool translate(TIntermBlock *root,
-                                 ShCompileOptions compileOptions,
+                                 const ShCompileOptions &compileOptions,
                                  PerformanceDiagnostics *perfDiagnostics) override;
     bool shouldFlattenPragmaStdglInvariantAll() override;
 
     // collectVariables needs to be run always so registers can be assigned.
-    bool shouldCollectVariables(ShCompileOptions compileOptions) override { return true; }
+    bool shouldCollectVariables(const ShCompileOptions &compileOptions) override { return true; }
 
     std::map<std::string, unsigned int> mShaderStorageBlockRegisterMap;
     std::map<std::string, unsigned int> mUniformBlockRegisterMap;
diff --git a/src/compiler/translator/TranslatorMetal.cpp b/src/compiler/translator/TranslatorMetal.cpp
index 372c742..01cf38e 100644
--- a/src/compiler/translator/TranslatorMetal.cpp
+++ b/src/compiler/translator/TranslatorMetal.cpp
@@ -107,7 +107,7 @@
 {}
 
 bool TranslatorMetal::translate(TIntermBlock *root,
-                                ShCompileOptions compileOptions,
+                                const ShCompileOptions &compileOptions,
                                 PerformanceDiagnostics *perfDiagnostics)
 {
     TInfoSinkBase sink;
@@ -153,9 +153,9 @@
     }
 
     // Initialize unused varying outputs to avoid spirv-cross dead-code removing them in later
-    // stage. Only do this if SH_INIT_OUTPUT_VARIABLES is not specified.
+    // stage. Only do this if initOutputVariables is not specified.
     if ((getShaderType() == GL_VERTEX_SHADER || getShaderType() == GL_GEOMETRY_SHADER_EXT) &&
-        (compileOptions & SH_INIT_OUTPUT_VARIABLES) == 0)
+        !compileOptions.initOutputVariables)
     {
         InitVariableList list;
         for (const sh::ShaderVariable &var : mOutputVaryings)
diff --git a/src/compiler/translator/TranslatorMetal.h b/src/compiler/translator/TranslatorMetal.h
index a160865..32b7564 100644
--- a/src/compiler/translator/TranslatorMetal.h
+++ b/src/compiler/translator/TranslatorMetal.h
@@ -28,7 +28,9 @@
 class SpecConstMetal : public SpecConst
 {
   public:
-    SpecConstMetal(TSymbolTable *symbolTable, ShCompileOptions compileOptions, GLenum shaderType)
+    SpecConstMetal(TSymbolTable *symbolTable,
+                   const ShCompileOptions &compileOptions,
+                   GLenum shaderType)
         : SpecConst(symbolTable, compileOptions, shaderType)
     {}
     ~SpecConstMetal() override {}
@@ -43,7 +45,7 @@
 
   protected:
     [[nodiscard]] bool translate(TIntermBlock *root,
-                                 ShCompileOptions compileOptions,
+                                 const ShCompileOptions &compileOptions,
                                  PerformanceDiagnostics *perfDiagnostics) override;
 
     [[nodiscard]] bool transformDepthBeforeCorrection(TIntermBlock *root,
diff --git a/src/compiler/translator/TranslatorMetalDirect.cpp b/src/compiler/translator/TranslatorMetalDirect.cpp
index 5dd882d..09c2dde 100644
--- a/src/compiler/translator/TranslatorMetalDirect.cpp
+++ b/src/compiler/translator/TranslatorMetalDirect.cpp
@@ -270,7 +270,7 @@
 }
 
 [[nodiscard]] bool InsertFragCoordCorrection(TCompiler *compiler,
-                                             ShCompileOptions compileOptions,
+                                             const ShCompileOptions &compileOptions,
                                              TIntermBlock *root,
                                              TIntermSequence *insertSequence,
                                              TSymbolTable *symbolTable,
@@ -608,7 +608,7 @@
 
 bool TranslatorMetalDirect::translateImpl(TInfoSinkBase &sink,
                                           TIntermBlock *root,
-                                          ShCompileOptions compileOptions,
+                                          const ShCompileOptions &compileOptions,
                                           PerformanceDiagnostics * /*perfDiagnostics*/,
                                           SpecConst *specConst,
                                           DriverUniformMetal *driverUniforms)
@@ -698,7 +698,7 @@
         return false;
     }
 
-    if (compileOptions & SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING)
+    if (compileOptions.emulateSeamfulCubeMapSampling)
     {
         if (!RewriteCubeMapSamplersAs2DArray(this, root, &symbolTable,
                                              getShaderType() == GL_FRAGMENT_SHADER))
@@ -1010,7 +1010,7 @@
         return false;
     }
 
-    const bool needsExplicitBoolCasts = (compileOptions & SH_ADD_EXPLICIT_BOOL_CASTS) != 0;
+    const bool needsExplicitBoolCasts = compileOptions.addExplicitBoolCasts;
     if (!AddExplicitTypeCasts(*this, *root, symbolEnv, needsExplicitBoolCasts))
     {
         return false;
@@ -1021,7 +1021,7 @@
         return false;
     }
 
-    if ((compileOptions & SH_REWRITE_ROW_MAJOR_MATRICES) != 0 && getShaderVersion() >= 300)
+    if (compileOptions.rewriteRowMajorMatrices && getShaderVersion() >= 300)
     {
         // "Make sure every uniform buffer variable has a name.  The following transformation
         // relies on this." This pass was removed in e196bc85ac2dda0e9f6664cfc2eca0029e33d2d1,
@@ -1098,7 +1098,7 @@
     {
         return false;
     }
-    if (!EmitMetal(*this, *root, idGen, pipelineStructs, symbolEnv, ppc, &getSymbolTable()))
+    if (!EmitMetal(*this, *root, idGen, pipelineStructs, symbolEnv, ppc, compileOptions))
     {
         return false;
     }
@@ -1109,7 +1109,7 @@
 }
 
 bool TranslatorMetalDirect::translate(TIntermBlock *root,
-                                      ShCompileOptions compileOptions,
+                                      const ShCompileOptions &compileOptions,
                                       PerformanceDiagnostics *perfDiagnostics)
 {
     if (!root)
diff --git a/src/compiler/translator/TranslatorMetalDirect.h b/src/compiler/translator/TranslatorMetalDirect.h
index 2719a1a..981204e 100644
--- a/src/compiler/translator/TranslatorMetalDirect.h
+++ b/src/compiler/translator/TranslatorMetalDirect.h
@@ -159,15 +159,15 @@
 
   protected:
     bool translate(TIntermBlock *root,
-                   ShCompileOptions compileOptions,
+                   const ShCompileOptions &compileOptions,
                    PerformanceDiagnostics *perfDiagnostics) override;
 
     // Need to collect variables so that RemoveInactiveInterfaceVariables works.
-    bool shouldCollectVariables(ShCompileOptions compileOptions) override { return true; }
+    bool shouldCollectVariables(const ShCompileOptions &compileOptions) override { return true; }
 
     [[nodiscard]] bool translateImpl(TInfoSinkBase &sink,
                                      TIntermBlock *root,
-                                     ShCompileOptions compileOptions,
+                                     const ShCompileOptions &compileOptions,
                                      PerformanceDiagnostics *perfDiagnostics,
                                      SpecConst *specConst,
                                      DriverUniformMetal *driverUniforms);
diff --git a/src/compiler/translator/TranslatorMetalDirect/EmitMetal.cpp b/src/compiler/translator/TranslatorMetalDirect/EmitMetal.cpp
index 470c431..0b4ea74 100644
--- a/src/compiler/translator/TranslatorMetalDirect/EmitMetal.cpp
+++ b/src/compiler/translator/TranslatorMetalDirect/EmitMetal.cpp
@@ -83,7 +83,7 @@
                       IdGen &idGen,
                       const PipelineStructs &pipelineStructs,
                       SymbolEnv &symbolEnv,
-                      TSymbolTable *symbolTable);
+                      const ShCompileOptions &compileOptions);
 
     void visitSymbol(TIntermSymbol *) override;
     void visitConstantUnion(TIntermConstantUnion *) override;
@@ -212,16 +212,16 @@
                                      IdGen &idGen,
                                      const PipelineStructs &pipelineStructs,
                                      SymbolEnv &symbolEnv,
-                                     TSymbolTable *symbolTable)
+                                     const ShCompileOptions &compileOptions)
     : TIntermTraverser(true, false, false),
       mOut(out),
       mCompiler(compiler),
       mPipelineStructs(pipelineStructs),
       mSymbolEnv(symbolEnv),
       mIdGen(idGen),
-      mMainUniformBufferIndex(symbolTable->getDefaultUniformsBindingIndex()),
-      mDriverUniformsBindingIndex(symbolTable->getDriverUniformsBindingIndex()),
-      mUBOArgumentBufferBindingIndex(symbolTable->getUBOArgumentBufferBindingIndex())
+      mMainUniformBufferIndex(compileOptions.metal.defaultUniformsBindingIndex),
+      mDriverUniformsBindingIndex(compileOptions.metal.driverUniformsBindingIndex),
+      mUBOArgumentBufferBindingIndex(compileOptions.metal.UBOArgumentBufferBindingIndex)
 {}
 
 void GenMetalTraverser::emitIndentation()
@@ -2481,7 +2481,7 @@
                    const PipelineStructs &pipelineStructs,
                    SymbolEnv &symbolEnv,
                    const ProgramPreludeConfig &ppc,
-                   TSymbolTable *symbolTable)
+                   const ShCompileOptions &compileOptions)
 {
     TInfoSinkBase &out = compiler.getInfoSink().obj;
 
@@ -2538,7 +2538,8 @@
 #else
         TInfoSinkBase &outWrapper = out;
 #endif
-        GenMetalTraverser gen(compiler, outWrapper, idGen, pipelineStructs, symbolEnv, symbolTable);
+        GenMetalTraverser gen(compiler, outWrapper, idGen, pipelineStructs, symbolEnv,
+                              compileOptions);
         root.traverse(&gen);
     }
 
diff --git a/src/compiler/translator/TranslatorMetalDirect/EmitMetal.h b/src/compiler/translator/TranslatorMetalDirect/EmitMetal.h
index 4c9fb42..f0236ea 100644
--- a/src/compiler/translator/TranslatorMetalDirect/EmitMetal.h
+++ b/src/compiler/translator/TranslatorMetalDirect/EmitMetal.h
@@ -24,7 +24,7 @@
                              const PipelineStructs &pipelineStructs,
                              SymbolEnv &symbolEnv,
                              const ProgramPreludeConfig &ppc,
-                             TSymbolTable *symbolTable);
+                             const ShCompileOptions &compileOptions);
 
 }  // namespace sh
 
diff --git a/src/compiler/translator/TranslatorVulkan.cpp b/src/compiler/translator/TranslatorVulkan.cpp
index 2275cd2..945c516 100644
--- a/src/compiler/translator/TranslatorVulkan.cpp
+++ b/src/compiler/translator/TranslatorVulkan.cpp
@@ -523,7 +523,7 @@
 }
 
 [[nodiscard]] bool AddVertexTransformationSupport(TCompiler *compiler,
-                                                  ShCompileOptions compileOptions,
+                                                  const ShCompileOptions &compileOptions,
                                                   TIntermBlock *root,
                                                   TSymbolTable *symbolTable,
                                                   SpecConst *specConst,
@@ -576,7 +576,7 @@
     TIntermTyped *w = new TIntermSwizzle(positionSymbol->deepCopy(), {3});
 
     TIntermTyped *transformedDepth = z;
-    if ((compileOptions & SH_ADD_VULKAN_DEPTH_CORRECTION) != 0)
+    if (compileOptions.addVulkanDepthCorrection)
     {
         TIntermBinary *zPlusW = new TIntermBinary(EOpAdd, z, w->deepCopy());
         TIntermBinary *halfZPlusW =
@@ -614,7 +614,7 @@
 
     // Create a call to ANGLETransformPosition, for the sole purpose of preventing it from being
     // culled as unused by glslang.
-    if ((compileOptions & SH_GENERATE_SPIRV_THROUGH_GLSLANG) != 0)
+    if (compileOptions.generateSpirvThroughGlslang)
     {
         TIntermSequence vec4Zero;
         vec4Zero.push_back(CreateZeroNode(*vec4Type));
@@ -630,7 +630,7 @@
 }
 
 [[nodiscard]] bool InsertFragCoordCorrection(TCompiler *compiler,
-                                             ShCompileOptions compileOptions,
+                                             const ShCompileOptions &compileOptions,
                                              TIntermBlock *root,
                                              TIntermSequence *insertSequence,
                                              TSymbolTable *symbolTable,
@@ -667,7 +667,7 @@
 
 bool TranslatorVulkan::translateImpl(TInfoSinkBase &sink,
                                      TIntermBlock *root,
-                                     ShCompileOptions compileOptions,
+                                     const ShCompileOptions &compileOptions,
                                      PerformanceDiagnostics * /*perfDiagnostics*/,
                                      SpecConst *specConst,
                                      DriverUniform *driverUniforms)
@@ -769,7 +769,7 @@
 
     // Rewrite samplerCubes as sampler2DArrays.  This must be done after rewriting struct samplers
     // as it doesn't expect that.
-    if ((compileOptions & SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING) != 0)
+    if (compileOptions.emulateSeamfulCubeMapSampling)
     {
         if (!RewriteCubeMapSamplersAs2DArray(this, root, &getSymbolTable(),
                                              getShaderType() == GL_FRAGMENT_SHADER))
@@ -879,7 +879,7 @@
 
     if (gl::ShaderTypeSupportsTransformFeedback(packedShaderType))
     {
-        if ((compileOptions & SH_ADD_VULKAN_XFB_EXTENSION_SUPPORT_CODE) != 0)
+        if (compileOptions.addVulkanXfbExtensionSupportCode)
         {
             // Add support code for transform feedback extension.
             if (!AddXfbExtensionSupport(this, root, &getSymbolTable(), driverUniforms))
@@ -1028,7 +1028,7 @@
             // check the existing input attachment variables and if there is no existing input
             // attachment variable then create a new one.
             if (getAdvancedBlendEquations().any() &&
-                (compileOptions & SH_ADD_ADVANCED_BLEND_EQUATIONS_EMULATION) != 0 &&
+                compileOptions.addAdvancedBlendEquationsEmulation &&
                 !EmulateAdvancedBlendEquations(this, compileOptions, root, &getSymbolTable(),
                                                driverUniforms, &mUniforms,
                                                getAdvancedBlendEquations()))
@@ -1093,7 +1093,7 @@
 
         case gl::ShaderType::Vertex:
         {
-            if ((compileOptions & SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE) != 0)
+            if (compileOptions.addVulkanXfbEmulationSupportCode)
             {
                 // Add support code for transform feedback emulation.  Only applies to vertex shader
                 // as tessellation and geometry shader transform feedback capture require
@@ -1175,7 +1175,8 @@
     return true;
 }
 
-void TranslatorVulkan::writeExtensionBehavior(ShCompileOptions compileOptions, TInfoSinkBase &sink)
+void TranslatorVulkan::writeExtensionBehavior(const ShCompileOptions &compileOptions,
+                                              TInfoSinkBase &sink)
 {
     const TExtensionBehavior &extBehavior = getExtensionBehavior();
     TBehavior multiviewBehavior           = EBhUndefined;
@@ -1215,7 +1216,7 @@
 }
 
 bool TranslatorVulkan::translate(TIntermBlock *root,
-                                 ShCompileOptions compileOptions,
+                                 const ShCompileOptions &compileOptions,
                                  PerformanceDiagnostics *perfDiagnostics)
 {
     TInfoSinkBase sink;
@@ -1225,8 +1226,7 @@
     DriverUniform driverUniforms(DriverUniformMode::InterfaceBlock);
     DriverUniformExtended driverUniformsExt(DriverUniformMode::InterfaceBlock);
 
-    const bool useExtendedDriverUniforms =
-        (compileOptions & SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE) != 0;
+    const bool useExtendedDriverUniforms = compileOptions.addVulkanXfbEmulationSupportCode;
 
     DriverUniform *uniforms = useExtendedDriverUniforms ? &driverUniformsExt : &driverUniforms;
 
@@ -1236,7 +1236,7 @@
     }
 
 #if defined(ANGLE_ENABLE_SPIRV_GENERATION_THROUGH_GLSLANG)
-    if ((compileOptions & SH_GENERATE_SPIRV_THROUGH_GLSLANG) != 0)
+    if (compileOptions.generateSpirvThroughGlslang)
     {
         // When generating text, glslang cannot know the precision of folded constants so it may
         // infer the wrong precisions.  The following transformation gives constants names with
@@ -1247,7 +1247,7 @@
             return false;
         }
 
-        const bool enablePrecision = (compileOptions & SH_IGNORE_PRECISION_QUALIFIERS) == 0;
+        const bool enablePrecision = !compileOptions.ignorePrecisionQualifiers;
 
         // Write translated shader.
         TOutputVulkanGLSL outputGLSL(this, sink, enablePrecision, compileOptions);
diff --git a/src/compiler/translator/TranslatorVulkan.h b/src/compiler/translator/TranslatorVulkan.h
index e2cd4af..db80f49 100644
--- a/src/compiler/translator/TranslatorVulkan.h
+++ b/src/compiler/translator/TranslatorVulkan.h
@@ -28,7 +28,7 @@
 
   protected:
     [[nodiscard]] bool translate(TIntermBlock *root,
-                                 ShCompileOptions compileOptions,
+                                 const ShCompileOptions &compileOptions,
                                  PerformanceDiagnostics *perfDiagnostics) override;
     bool shouldFlattenPragmaStdglInvariantAll() override;
 
@@ -36,12 +36,12 @@
     // See TranslatorMetal.cpp.
     [[nodiscard]] bool translateImpl(TInfoSinkBase &sink,
                                      TIntermBlock *root,
-                                     ShCompileOptions compileOptions,
+                                     const ShCompileOptions &compileOptions,
                                      PerformanceDiagnostics *perfDiagnostics,
                                      SpecConst *specConst,
                                      DriverUniform *driverUniforms);
 
-    void writeExtensionBehavior(ShCompileOptions compileOptions, TInfoSinkBase &sink);
+    void writeExtensionBehavior(const ShCompileOptions &compileOptions, TInfoSinkBase &sink);
 
     // Give subclass such as TranslatorMetal a chance to do depth transform before
     // TranslatorVulkan apply its own transform.
diff --git a/src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.cpp b/src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.cpp
index efa22d2..8c29259 100644
--- a/src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.cpp
+++ b/src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.cpp
@@ -127,7 +127,7 @@
                                                  TIntermBlock *root,
                                                  unsigned numberOfViews,
                                                  GLenum shaderType,
-                                                 ShCompileOptions compileOptions,
+                                                 const ShCompileOptions &compileOptions,
                                                  ShShaderOutput shaderOutput,
                                                  TSymbolTable *symbolTable)
 {
@@ -163,7 +163,7 @@
 
         // The AST transformation which adds the expression to select the viewport index should
         // be done only for the GLSL and ESSL output.
-        const bool selectView = (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0;
+        const bool selectView = compileOptions.selectViewInNvGLSLVertexShader;
         // Assert that if the view is selected in the vertex shader, then the output is
         // either GLSL or ESSL.
         ASSERT(!selectView || IsOutputGLSL(shaderOutput) || IsOutputESSL(shaderOutput));
diff --git a/src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.h b/src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.h
index cb63553..49a964d 100644
--- a/src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.h
+++ b/src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.h
@@ -13,7 +13,7 @@
 // its qualifier to EvqTemporary.
 // - Add initializers of ViewID_OVR and InstanceID to the beginning of the body of main. The pass
 // should be executed before any variables get collected so that usage of gl_InstanceID is recorded.
-// - If the output is ESSL or GLSL and the SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is
+// - If the output is ESSL or GLSL and the selectViewInNvGLSLVertexShader option is
 // enabled, the expression
 // "if (multiviewBaseViewLayerIndex < 0) {
 //      gl_ViewportIndex = int(ViewID_OVR);
@@ -38,13 +38,14 @@
 class TIntermBlock;
 class TSymbolTable;
 
-[[nodiscard]] bool DeclareAndInitBuiltinsForInstancedMultiview(TCompiler *compiler,
-                                                               TIntermBlock *root,
-                                                               unsigned numberOfViews,
-                                                               GLenum shaderType,
-                                                               ShCompileOptions compileOptions,
-                                                               ShShaderOutput shaderOutput,
-                                                               TSymbolTable *symbolTable);
+[[nodiscard]] bool DeclareAndInitBuiltinsForInstancedMultiview(
+    TCompiler *compiler,
+    TIntermBlock *root,
+    unsigned numberOfViews,
+    GLenum shaderType,
+    const ShCompileOptions &compileOptions,
+    ShShaderOutput shaderOutput,
+    TSymbolTable *symbolTable);
 
 }  // namespace sh
 
diff --git a/src/compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.cpp b/src/compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.cpp
index dcc0d36..11c8b72 100644
--- a/src/compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.cpp
+++ b/src/compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.cpp
@@ -242,7 +242,7 @@
   public:
     explicit MonomorphizeTraverser(TCompiler *compiler,
                                    TSymbolTable *symbolTable,
-                                   ShCompileOptions compileOptions,
+                                   const ShCompileOptions &compileOptions,
                                    UnsupportedFunctionArgsBitSet unsupportedFunctionArgs,
                                    FunctionMap *functionMap)
         : TIntermTraverser(true, false, false, symbolTable),
@@ -345,8 +345,7 @@
         {
             // Monomorphize if the opaque uniform is a samplerCube and ES2's cube sampling emulation
             // is requested.
-            if (type.isSamplerCube() &&
-                (mCompileOptions & SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING) != 0)
+            if (type.isSamplerCube() && mCompileOptions.emulateSeamfulCubeMapSampling)
             {
                 return true;
             }
@@ -435,7 +434,7 @@
     }
 
     TCompiler *mCompiler;
-    ShCompileOptions mCompileOptions;
+    const ShCompileOptions &mCompileOptions;
     UnsupportedFunctionArgsBitSet mUnsupportedFunctionArgs;
     bool mAnyMonomorphized = false;
 
@@ -555,7 +554,7 @@
 bool MonomorphizeUnsupportedFunctionsImpl(TCompiler *compiler,
                                           TIntermBlock *root,
                                           TSymbolTable *symbolTable,
-                                          ShCompileOptions compileOptions,
+                                          const ShCompileOptions &compileOptions,
                                           UnsupportedFunctionArgsBitSet unsupportedFunctionArgs)
 {
     // First, sort out the declarations such that all non-function declarations are placed before
@@ -598,7 +597,7 @@
 bool MonomorphizeUnsupportedFunctions(TCompiler *compiler,
                                       TIntermBlock *root,
                                       TSymbolTable *symbolTable,
-                                      ShCompileOptions compileOptions,
+                                      const ShCompileOptions &compileOptions,
                                       UnsupportedFunctionArgsBitSet unsupportedFunctionArgs)
 {
     // This function actually applies multiple transformation, and the AST may not be valid until
diff --git a/src/compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.h b/src/compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.h
index 214cdab..a9a2127 100644
--- a/src/compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.h
+++ b/src/compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.h
@@ -47,7 +47,7 @@
 [[nodiscard]] bool MonomorphizeUnsupportedFunctions(TCompiler *compiler,
                                                     TIntermBlock *root,
                                                     TSymbolTable *symbolTable,
-                                                    ShCompileOptions compileOptions,
+                                                    const ShCompileOptions &compileOptions,
                                                     UnsupportedFunctionArgsBitSet);
 }  // namespace sh
 
diff --git a/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.cpp b/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.cpp
index d0dadc7..a5e20eb 100644
--- a/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.cpp
+++ b/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.cpp
@@ -143,7 +143,7 @@
         {
             TType *structSpecifierType      = new TType(type.getStruct(), true);
             TVariable *emptyVariable        = new TVariable(mSymbolTable, kEmptyImmutableString,
-                                                     structSpecifierType, SymbolType::Empty);
+                                                            structSpecifierType, SymbolType::Empty);
             TIntermDeclaration *declaration = new TIntermDeclaration();
             declaration->appendDeclarator(new TIntermSymbol(emptyVariable));
             replacement.push_back(declaration);
@@ -158,7 +158,7 @@
 
 bool RemoveInactiveInterfaceVariablesTraverser::visitBinary(Visit visit, TIntermBinary *node)
 {
-    // Remove any code that SH_INIT_OUTPUT_VARIABLES might have added corresponding to inactive
+    // Remove any code that initOutputVariables might have added corresponding to inactive
     // output variables.  This code is always in the form of `variable = ...;`.
     if (node->getOp() != EOpAssign)
     {
diff --git a/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp b/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp
index a9fdd7b..f782604 100644
--- a/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp
+++ b/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp
@@ -58,9 +58,10 @@
 // Either: GL_NV_fragment_shader_interlock,
 //         GL_INTEL_fragment_shader_ordering
 //         GL_ARB_fragment_shader_interlock,
-static TIntermNode *CreateBuiltInInterlockBeginCall(TCompiler *compiler, TSymbolTable &symbolTable)
+static TIntermNode *CreateBuiltInInterlockBeginCall(const ShCompileOptions &compileOptions,
+                                                    TSymbolTable &symbolTable)
 {
-    switch (compiler->getResources().FragmentSynchronizationType)
+    switch (compileOptions.pls.fragmentSynchronizationType)
     {
         case ShFragmentSynchronizationType::FragmentShaderInterlock_NV_GL:
             return CreateBuiltInFunctionCallNode("beginInvocationInterlockNV", {}, symbolTable,
@@ -80,9 +81,10 @@
 //
 // Either: GL_ARB_fragment_shader_interlock or GL_NV_fragment_shader_interlock.
 // GL_INTEL_fragment_shader_ordering doesn't have an "end()" call.
-static TIntermNode *CreateBuiltInInterlockEndCall(TCompiler *compiler, TSymbolTable &symbolTable)
+static TIntermNode *CreateBuiltInInterlockEndCall(const ShCompileOptions &compileOptions,
+                                                  TSymbolTable &symbolTable)
 {
-    switch (compiler->getResources().FragmentSynchronizationType)
+    switch (compileOptions.pls.fragmentSynchronizationType)
     {
         case ShFragmentSynchronizationType::FragmentShaderInterlock_NV_GL:
             return CreateBuiltInFunctionCallNode("endInvocationInterlockNV", {}, symbolTable,
@@ -338,7 +340,7 @@
 bool RewritePixelLocalStorageToImages(TCompiler *compiler,
                                       TIntermBlock *root,
                                       TSymbolTable &symbolTable,
-                                      ShCompileOptions compileOptions,
+                                      const ShCompileOptions &compileOptions,
                                       int shaderVersion)
 {
     // If any functions take PLS arguments, monomorphize the functions by removing said parameters
@@ -356,7 +358,7 @@
 
     // Surround the critical section of PLS operations in fragment synchronization calls, if
     // supported. This makes pixel local storage coherent.
-    TIntermNode *interlockBeginCall = CreateBuiltInInterlockBeginCall(compiler, symbolTable);
+    TIntermNode *interlockBeginCall = CreateBuiltInInterlockBeginCall(compileOptions, symbolTable);
     if (interlockBeginCall)
     {
         // TODO(anglebug.com/7279): Inject these functions in a tight critical section, instead of
@@ -364,7 +366,7 @@
         //   - Monomorphize all PLS calls into main().
         //   - Insert begin/end calls around the first/last PLS calls (and outside of flow control).
         mainBody->insertStatement(0, interlockBeginCall);
-        TIntermNode *interlockEndCall = CreateBuiltInInterlockEndCall(compiler, symbolTable);
+        TIntermNode *interlockEndCall = CreateBuiltInInterlockEndCall(compileOptions, symbolTable);
         if (interlockEndCall)
         {
             // Not all fragment synchronization extensions have an end() call.
diff --git a/src/compiler/translator/tree_ops/RewritePixelLocalStorage.h b/src/compiler/translator/tree_ops/RewritePixelLocalStorage.h
index 1f33e23..8b58465 100644
--- a/src/compiler/translator/tree_ops/RewritePixelLocalStorage.h
+++ b/src/compiler/translator/tree_ops/RewritePixelLocalStorage.h
@@ -7,7 +7,7 @@
 #ifndef COMPILER_TRANSLATOR_TREEOPS_REWRITE_PIXELLOCALSTORAGE_H_
 #define COMPILER_TRANSLATOR_TREEOPS_REWRITE_PIXELLOCALSTORAGE_H_
 
-#include <GLSLANG/ShaderVars.h>
+#include <GLSLANG/ShaderLang.h>
 
 namespace sh
 {
@@ -21,7 +21,7 @@
 [[nodiscard]] bool RewritePixelLocalStorageToImages(TCompiler *compiler,
                                                     TIntermBlock *root,
                                                     TSymbolTable &symbolTable,
-                                                    ShCompileOptions compileOptions,
+                                                    const ShCompileOptions &compileOptions,
                                                     int shaderVersion);
 
 }  // namespace sh
diff --git a/src/compiler/translator/tree_ops/vulkan/EmulateAdvancedBlendEquations.cpp b/src/compiler/translator/tree_ops/vulkan/EmulateAdvancedBlendEquations.cpp
index 691cd3d..4a4294c 100644
--- a/src/compiler/translator/tree_ops/vulkan/EmulateAdvancedBlendEquations.cpp
+++ b/src/compiler/translator/tree_ops/vulkan/EmulateAdvancedBlendEquations.cpp
@@ -33,7 +33,7 @@
 {
   public:
     Builder(TCompiler *compiler,
-            ShCompileOptions compileOptions,
+            const ShCompileOptions &compileOptions,
             TSymbolTable *symbolTable,
             const DriverUniform *driverUniforms,
             std::vector<ShaderVariable> *uniforms,
@@ -60,7 +60,7 @@
     void generateEquationSwitch(TIntermBlock *blendBlock);
 
     TCompiler *mCompiler;
-    ShCompileOptions mCompileOptions;
+    const ShCompileOptions &mCompileOptions;
     TSymbolTable *mSymbolTable;
     const DriverUniform *mDriverUniforms;
     std::vector<ShaderVariable> *mUniforms;
@@ -1041,14 +1041,14 @@
     TType *vec3Type            = new TType(EbtFloat, precision, EvqTemporary, 3);
 
     // symbol = vec3(0)
-    // If alpha != 0 assign symbol based on SH_PRECISION_SAFE_DIVISION compile option.
+    // If alpha != 0 assign symbol based on precisionSafeDivision compile option.
     TIntermTyped *alpha            = new TIntermSwizzle(var, {3});
     TIntermSymbol *symbol          = MakeVariable(mSymbolTable, name, vec3Type);
     TIntermTyped *alphaNotZero     = new TIntermBinary(EOpNotEqual, alpha, Float(0));
     TIntermBlock *rgbDivAlphaBlock = new TIntermBlock;
 
     constexpr int kColorChannels = 3;
-    if ((mCompileOptions & SH_PRECISION_SAFE_DIVISION) != 0)
+    if (mCompileOptions.precisionSafeDivision)
     {
         // For each component:
         // symbol.x = (var.x == var.w) ? 1.0 : var.x / var.w
@@ -1260,7 +1260,7 @@
 }  // anonymous namespace
 
 bool EmulateAdvancedBlendEquations(TCompiler *compiler,
-                                   ShCompileOptions compileOptions,
+                                   const ShCompileOptions &compileOptions,
                                    TIntermBlock *root,
                                    TSymbolTable *symbolTable,
                                    const DriverUniform *driverUniforms,
diff --git a/src/compiler/translator/tree_ops/vulkan/EmulateAdvancedBlendEquations.h b/src/compiler/translator/tree_ops/vulkan/EmulateAdvancedBlendEquations.h
index c65dc06..5631ca8 100644
--- a/src/compiler/translator/tree_ops/vulkan/EmulateAdvancedBlendEquations.h
+++ b/src/compiler/translator/tree_ops/vulkan/EmulateAdvancedBlendEquations.h
@@ -28,7 +28,7 @@
 // which function to use at runtime.
 [[nodiscard]] bool EmulateAdvancedBlendEquations(
     TCompiler *compiler,
-    ShCompileOptions compileOptions,
+    const ShCompileOptions &compileOptions,
     TIntermBlock *root,
     TSymbolTable *symbolTable,
     const DriverUniform *driverUniforms,
diff --git a/src/compiler/translator/tree_ops/vulkan/EmulateDithering.cpp b/src/compiler/translator/tree_ops/vulkan/EmulateDithering.cpp
index d0f91ce..550dbdc 100644
--- a/src/compiler/translator/tree_ops/vulkan/EmulateDithering.cpp
+++ b/src/compiler/translator/tree_ops/vulkan/EmulateDithering.cpp
@@ -69,7 +69,7 @@
 }
 
 void EmitFragmentOutputDither(TCompiler *compiler,
-                              ShCompileOptions compileOptions,
+                              const ShCompileOptions &compileOptions,
                               TSymbolTable *symbolTable,
                               TIntermBlock *ditherBlock,
                               TIntermTyped *ditherControl,
@@ -77,7 +77,7 @@
                               TIntermTyped *fragmentOutput,
                               uint32_t location)
 {
-    bool roundOutputAfterDithering = (compileOptions & SH_ROUND_OUTPUT_AFTER_DITHERING) != 0;
+    bool roundOutputAfterDithering = compileOptions.roundOutputAfterDithering;
 
     // dither >> 2*location
     TIntermBinary *ditherControlShifted = new TIntermBinary(
@@ -234,7 +234,7 @@
 }
 
 void EmitFragmentVariableDither(TCompiler *compiler,
-                                ShCompileOptions compileOptions,
+                                const ShCompileOptions &compileOptions,
                                 TSymbolTable *symbolTable,
                                 TIntermBlock *ditherBlock,
                                 TIntermTyped *ditherControl,
@@ -273,7 +273,7 @@
 }
 
 TIntermNode *EmitDitheringBlock(TCompiler *compiler,
-                                ShCompileOptions compileOptions,
+                                const ShCompileOptions &compileOptions,
                                 TSymbolTable *symbolTable,
                                 SpecConst *specConst,
                                 DriverUniform *driverUniforms,
@@ -434,7 +434,7 @@
 }  // anonymous namespace
 
 bool EmulateDithering(TCompiler *compiler,
-                      ShCompileOptions compileOptions,
+                      const ShCompileOptions &compileOptions,
                       TIntermBlock *root,
                       TSymbolTable *symbolTable,
                       SpecConst *specConst,
diff --git a/src/compiler/translator/tree_ops/vulkan/EmulateDithering.h b/src/compiler/translator/tree_ops/vulkan/EmulateDithering.h
index 829a6e3..9ea0a51 100644
--- a/src/compiler/translator/tree_ops/vulkan/EmulateDithering.h
+++ b/src/compiler/translator/tree_ops/vulkan/EmulateDithering.h
@@ -22,7 +22,7 @@
 class DriverUniform;
 
 [[nodiscard]] bool EmulateDithering(TCompiler *compiler,
-                                    ShCompileOptions compileOptions,
+                                    const ShCompileOptions &compileOptions,
                                     TIntermBlock *root,
                                     TSymbolTable *symbolTable,
                                     SpecConst *specConst,
diff --git a/src/compiler/translator/tree_util/IntermNodePatternMatcher.h b/src/compiler/translator/tree_util/IntermNodePatternMatcher.h
index 6f3d873..b8321b9 100644
--- a/src/compiler/translator/tree_util/IntermNodePatternMatcher.h
+++ b/src/compiler/translator/tree_util/IntermNodePatternMatcher.h
@@ -53,7 +53,7 @@
         kArrayLengthMethod = 1u << 6u,
 
         // Matches a vector or matrix constructor whose arguments are scalarized by the
-        // SH_SCALARIZE_VEC_OR_MAT_CONSTRUCTOR_ARGUMENTS workaround.
+        // scalarizeVecOrMatConstructorArguments workaround.
         kScalarizedVecOrMatConstructor = 1u << 7u,
     };
     IntermNodePatternMatcher(const unsigned int mask);
diff --git a/src/compiler/translator/tree_util/SpecializationConstant.cpp b/src/compiler/translator/tree_util/SpecializationConstant.cpp
index 97e46cc..6a72548 100644
--- a/src/compiler/translator/tree_util/SpecializationConstant.cpp
+++ b/src/compiler/translator/tree_util/SpecializationConstant.cpp
@@ -38,7 +38,9 @@
 }
 }  // anonymous namespace
 
-SpecConst::SpecConst(TSymbolTable *symbolTable, ShCompileOptions compileOptions, GLenum shaderType)
+SpecConst::SpecConst(TSymbolTable *symbolTable,
+                     const ShCompileOptions &compileOptions,
+                     GLenum shaderType)
     : mSymbolTable(symbolTable),
       mCompileOptions(compileOptions),
       mSurfaceRotationVar(nullptr),
@@ -50,7 +52,7 @@
     }
 
     // Mark SpecConstUsage::Rotation unconditionally.  gl_Position is always rotated.
-    if ((mCompileOptions & SH_USE_SPECIALIZATION_CONSTANT) != 0)
+    if (mCompileOptions.useSpecializationConstant)
     {
         mUsageBits.set(vk::SpecConstUsage::Rotation);
     }
@@ -96,7 +98,7 @@
 
 TIntermTyped *SpecConst::getSwapXY()
 {
-    if ((mCompileOptions & SH_USE_SPECIALIZATION_CONSTANT) == 0)
+    if (!mCompileOptions.useSpecializationConstant)
     {
         return nullptr;
     }
diff --git a/src/compiler/translator/tree_util/SpecializationConstant.h b/src/compiler/translator/tree_util/SpecializationConstant.h
index b7e018c..6644706 100644
--- a/src/compiler/translator/tree_util/SpecializationConstant.h
+++ b/src/compiler/translator/tree_util/SpecializationConstant.h
@@ -24,7 +24,7 @@
 class SpecConst
 {
   public:
-    SpecConst(TSymbolTable *symbolTable, ShCompileOptions compileOptions, GLenum shaderType);
+    SpecConst(TSymbolTable *symbolTable, const ShCompileOptions &compileOptions, GLenum shaderType);
     virtual ~SpecConst();
 
     // Flip/rotation
@@ -42,7 +42,7 @@
 
     // If unsupported, this should be set to null.
     TSymbolTable *mSymbolTable;
-    ShCompileOptions mCompileOptions;
+    const ShCompileOptions &mCompileOptions;
 
     TVariable *mSurfaceRotationVar;
     TVariable *mDitherVar;
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index 74440d9..7c9a00b 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -390,11 +390,6 @@
     // ES 3.2 Table 20.41: Implementation Dependent Values (cont.)
     GLint maxTextureBufferSize         = 0;
     GLint textureBufferOffsetAlignment = 0;
-
-    // Direct-to-metal constants:
-    GLuint driverUniformsBindingIndex    = 0;
-    GLuint defaultUniformsBindingIndex   = 0;
-    GLuint UBOArgumentBufferBindingIndex = 0;
 };
 
 Caps GenerateMinimumCaps(const Version &clientVersion, const Extensions &extensions);
diff --git a/src/libANGLE/Compiler.cpp b/src/libANGLE/Compiler.cpp
index 90e5397..4088924 100644
--- a/src/libANGLE/Compiler.cpp
+++ b/src/libANGLE/Compiler.cpp
@@ -299,15 +299,6 @@
 
     // Subpixel bits.
     mResources.SubPixelBits = static_cast<int>(caps.subPixelBits);
-
-    // Direct-to-metal constants:
-    mResources.DriverUniformsBindingIndex    = caps.driverUniformsBindingIndex;
-    mResources.DefaultUniformsBindingIndex   = caps.defaultUniformsBindingIndex;
-    mResources.UBOArgumentBufferBindingIndex = caps.UBOArgumentBufferBindingIndex;
-
-    // For ANGLE_shader_pixel_local_storage_coherent.
-    mResources.FragmentSynchronizationType =
-        mImplementation->getBackendFeatures().fragmentSynchronizationType;
 }
 
 Compiler::~Compiler() = default;
diff --git a/src/libANGLE/Shader.cpp b/src/libANGLE/Shader.cpp
index 1e4e254..928b116 100644
--- a/src/libANGLE/Shader.cpp
+++ b/src/libANGLE/Shader.cpp
@@ -349,23 +349,26 @@
     mState.mCompileStatus = CompileStatus::COMPILE_REQUESTED;
     mBoundCompiler.set(context, context->getCompiler());
 
-    ShCompileOptions options = (SH_OBJECT_CODE | SH_VARIABLES | SH_EMULATE_GL_DRAW_ID);
+    ShCompileOptions options = {};
+    options.objectCode       = true;
+    options.variables        = true;
+    options.emulateGLDrawID  = true;
 
     // Add default options to WebGL shaders to prevent unexpected behavior during
     // compilation.
     if (context->isWebGL())
     {
-        options |= SH_INIT_GL_POSITION;
-        options |= SH_LIMIT_CALL_STACK_DEPTH;
-        options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
-        options |= SH_ENFORCE_PACKING_RESTRICTIONS;
-        options |= SH_INIT_SHARED_VARIABLES;
+        options.initGLPosition             = true;
+        options.limitCallStackDepth        = true;
+        options.limitExpressionComplexity  = true;
+        options.enforcePackingRestrictions = true;
+        options.initSharedVariables        = true;
     }
     else
     {
         // Per https://github.com/KhronosGroup/WebGL/pull/3278 gl_BaseVertex/gl_BaseInstance are
         // removed from WebGL
-        options |= SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE;
+        options.emulateGLBaseVertexBaseInstance = true;
     }
 
     // Some targets (e.g. D3D11 Feature Level 9_3 and below) do not support non-constant loop
@@ -373,18 +376,18 @@
     // message we can instruct the compiler to pre-validate.
     if (mRendererLimitations.shadersRequireIndexedLoopValidation)
     {
-        options |= SH_VALIDATE_LOOP_INDEXING;
+        options.validateLoopIndexing = true;
     }
 
     if (context->getFrontendFeatures().scalarizeVecAndMatConstructorArgs.enabled)
     {
-        options |= SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS;
+        options.scalarizeVecAndMatConstructorArgs = true;
     }
 
     if (context->getFrontendFeatures().forceInitShaderVariables.enabled)
     {
-        options |= SH_INIT_OUTPUT_VARIABLES;
-        options |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
+        options.initOutputVariables           = true;
+        options.initializeUninitializedLocals = true;
     }
 
     mCurrentMaxComputeWorkGroupInvocations =
@@ -401,7 +404,7 @@
     mCompilingState.reset(new CompilingState());
     mCompilingState->shCompilerInstance = std::move(compilerInstance);
     mCompilingState->compileEvent =
-        mImplementation->compile(context, &(mCompilingState->shCompilerInstance), options);
+        mImplementation->compile(context, &(mCompilingState->shCompilerInstance), &options);
 }
 
 void Shader::resolveCompile()
diff --git a/src/libANGLE/renderer/CompilerImpl.h b/src/libANGLE/renderer/CompilerImpl.h
index 8c1803e..0e8c519 100644
--- a/src/libANGLE/renderer/CompilerImpl.h
+++ b/src/libANGLE/renderer/CompilerImpl.h
@@ -17,12 +17,6 @@
 namespace rx
 {
 
-struct CompilerBackendFeatures
-{
-    // For ANGLE_shader_pixel_local_storage_coherent.
-    ShFragmentSynchronizationType fragmentSynchronizationType = ShFragmentSynchronizationType::None;
-};
-
 class CompilerImpl : angle::NonCopyable
 {
   public:
@@ -31,15 +25,8 @@
 
     // TODO(jmadill): Expose translator built-in resources init method.
     virtual ShShaderOutput getTranslatorOutputType() const = 0;
-
-    virtual CompilerBackendFeatures getBackendFeatures() const;
 };
 
-inline CompilerBackendFeatures CompilerImpl::getBackendFeatures() const
-{
-    return CompilerBackendFeatures();
-}
-
 }  // namespace rx
 
 #endif  // LIBANGLE_RENDERER_COMPILERIMPL_H_
diff --git a/src/libANGLE/renderer/ShaderImpl.cpp b/src/libANGLE/renderer/ShaderImpl.cpp
index 59ade77..eb20adc 100644
--- a/src/libANGLE/renderer/ShaderImpl.cpp
+++ b/src/libANGLE/renderer/ShaderImpl.cpp
@@ -41,7 +41,7 @@
 class TranslateTask : public angle::Closure
 {
   public:
-    TranslateTask(ShHandle handle, ShCompileOptions options, const std::string &source)
+    TranslateTask(ShHandle handle, const ShCompileOptions &options, const std::string &source)
         : mHandle(handle), mOptions(options), mSource(source), mResult(false)
     {}
 
@@ -83,15 +83,15 @@
     const gl::Context *context,
     gl::ShCompilerInstance *compilerInstance,
     const std::string &source,
-    ShCompileOptions compileOptions)
+    ShCompileOptions *compileOptions)
 {
 #if defined(ANGLE_ENABLE_ASSERTS)
-    compileOptions |= SH_VALIDATE_AST;
+    compileOptions->validateAST = true;
 #endif
 
     auto workerThreadPool = context->getShaderCompileThreadPool();
     auto translateTask =
-        std::make_shared<TranslateTask>(compilerInstance->getHandle(), compileOptions, source);
+        std::make_shared<TranslateTask>(compilerInstance->getHandle(), *compileOptions, source);
 
     return std::make_shared<WaitableCompileEventImpl>(
         angle::WorkerThreadPool::PostWorkerTask(workerThreadPool, translateTask), translateTask);
diff --git a/src/libANGLE/renderer/ShaderImpl.h b/src/libANGLE/renderer/ShaderImpl.h
index e09467b..be50cc7 100644
--- a/src/libANGLE/renderer/ShaderImpl.h
+++ b/src/libANGLE/renderer/ShaderImpl.h
@@ -55,7 +55,7 @@
 
     virtual std::shared_ptr<WaitableCompileEvent> compile(const gl::Context *context,
                                                           gl::ShCompilerInstance *compilerInstance,
-                                                          ShCompileOptions options) = 0;
+                                                          ShCompileOptions *options) = 0;
 
     virtual std::string getDebugInfo() const = 0;
 
@@ -67,7 +67,7 @@
     std::shared_ptr<WaitableCompileEvent> compileImpl(const gl::Context *context,
                                                       gl::ShCompilerInstance *compilerInstance,
                                                       const std::string &source,
-                                                      ShCompileOptions compileOptions);
+                                                      ShCompileOptions *compileOptions);
 
     const gl::ShaderState &mState;
 };
diff --git a/src/libANGLE/renderer/d3d/ShaderD3D.cpp b/src/libANGLE/renderer/d3d/ShaderD3D.cpp
index d63a01b..da0bc6f 100644
--- a/src/libANGLE/renderer/d3d/ShaderD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ShaderD3D.cpp
@@ -26,7 +26,7 @@
 {
   public:
     TranslateTaskD3D(ShHandle handle,
-                     ShCompileOptions options,
+                     const ShCompileOptions &options,
                      const std::string &source,
                      const std::string &sourcePath)
         : mHandle(handle),
@@ -88,52 +88,10 @@
     std::shared_ptr<TranslateTaskD3D> mTranslateTask;
 };
 
-ShaderD3D::ShaderD3D(const gl::ShaderState &state,
-                     const angle::FeaturesD3D &features,
-                     const gl::Extensions &extensions)
-    : ShaderImpl(state), mAdditionalOptions(0)
+ShaderD3D::ShaderD3D(const gl::ShaderState &state, RendererD3D *renderer)
+    : ShaderImpl(state), mRenderer(renderer)
 {
     uncompile();
-
-    if (features.expandIntegerPowExpressions.enabled)
-    {
-        mAdditionalOptions |= SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS;
-    }
-
-    if (features.getDimensionsIgnoresBaseLevel.enabled)
-    {
-        mAdditionalOptions |= SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL;
-    }
-
-    if (features.preAddTexelFetchOffsets.enabled)
-    {
-        mAdditionalOptions |= SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH;
-    }
-    if (features.rewriteUnaryMinusOperator.enabled)
-    {
-        mAdditionalOptions |= SH_REWRITE_INTEGER_UNARY_MINUS_OPERATOR;
-    }
-    if (features.emulateIsnanFloat.enabled)
-    {
-        mAdditionalOptions |= SH_EMULATE_ISNAN_FLOAT_FUNCTION;
-    }
-    if (features.skipVSConstantRegisterZero.enabled &&
-        mState.getShaderType() == gl::ShaderType::Vertex)
-    {
-        mAdditionalOptions |= SH_SKIP_D3D_CONSTANT_REGISTER_ZERO;
-    }
-    if (features.forceAtomicValueResolution.enabled)
-    {
-        mAdditionalOptions |= SH_FORCE_ATOMIC_VALUE_RESOLUTION;
-    }
-    if (features.allowTranslateUniformBlockToStructuredBuffer.enabled)
-    {
-        mAdditionalOptions |= SH_ALLOW_TRANSLATE_UNIFORM_BLOCK_TO_STRUCTUREDBUFFER;
-    }
-    if (extensions.multiviewOVR || extensions.multiview2OVR)
-    {
-        mAdditionalOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
-    }
 }
 
 ShaderD3D::~ShaderD3D() {}
@@ -269,12 +227,13 @@
 
 std::shared_ptr<WaitableCompileEvent> ShaderD3D::compile(const gl::Context *context,
                                                          gl::ShCompilerInstance *compilerInstance,
-                                                         ShCompileOptions options)
+                                                         ShCompileOptions *options)
 {
     std::string sourcePath;
     uncompile();
 
-    ShCompileOptions additionalOptions = 0;
+    const angle::FeaturesD3D &features = mRenderer->getFeatures();
+    const gl::Extensions &extensions   = mRenderer->getNativeExtensions();
 
     const std::string &source = mState.getSource();
 
@@ -283,13 +242,50 @@
     {
         sourcePath = angle::CreateTemporaryFile().value();
         writeFile(sourcePath.c_str(), source.c_str(), source.length());
-        additionalOptions |= SH_LINE_DIRECTIVES | SH_SOURCE_PATH;
+        options->lineDirectives = true;
+        options->sourcePath     = true;
     }
 #endif
 
-    additionalOptions |= mAdditionalOptions;
+    if (features.expandIntegerPowExpressions.enabled)
+    {
+        options->expandSelectHLSLIntegerPowExpressions = true;
+    }
 
-    options |= additionalOptions;
+    if (features.getDimensionsIgnoresBaseLevel.enabled)
+    {
+        options->HLSLGetDimensionsIgnoresBaseLevel = true;
+    }
+
+    if (features.preAddTexelFetchOffsets.enabled)
+    {
+        options->rewriteTexelFetchOffsetToTexelFetch = true;
+    }
+    if (features.rewriteUnaryMinusOperator.enabled)
+    {
+        options->rewriteIntegerUnaryMinusOperator = true;
+    }
+    if (features.emulateIsnanFloat.enabled)
+    {
+        options->emulateIsnanFloatFunction = true;
+    }
+    if (features.skipVSConstantRegisterZero.enabled &&
+        mState.getShaderType() == gl::ShaderType::Vertex)
+    {
+        options->skipD3DConstantRegisterZero = true;
+    }
+    if (features.forceAtomicValueResolution.enabled)
+    {
+        options->forceAtomicValueResolution = true;
+    }
+    if (features.allowTranslateUniformBlockToStructuredBuffer.enabled)
+    {
+        options->allowTranslateUniformBlockToStructuredBuffer = true;
+    }
+    if (extensions.multiviewOVR || extensions.multiview2OVR)
+    {
+        options->initializeBuiltinsForInstancedMultiview = true;
+    }
 
     auto postTranslateFunctor = [this](gl::ShCompilerInstance *compiler, std::string *infoLog) {
         // TODO(jmadill): We shouldn't need to cache this.
@@ -369,7 +365,7 @@
     };
 
     auto workerThreadPool = context->getWorkerThreadPool();
-    auto translateTask = std::make_shared<TranslateTaskD3D>(compilerInstance->getHandle(), options,
+    auto translateTask = std::make_shared<TranslateTaskD3D>(compilerInstance->getHandle(), *options,
                                                             source, sourcePath);
 
     return std::make_shared<WaitableCompileEventD3D>(
diff --git a/src/libANGLE/renderer/d3d/ShaderD3D.h b/src/libANGLE/renderer/d3d/ShaderD3D.h
index f498709..d1c5622 100644
--- a/src/libANGLE/renderer/d3d/ShaderD3D.h
+++ b/src/libANGLE/renderer/d3d/ShaderD3D.h
@@ -44,14 +44,12 @@
 class ShaderD3D : public ShaderImpl
 {
   public:
-    ShaderD3D(const gl::ShaderState &state,
-              const angle::FeaturesD3D &features,
-              const gl::Extensions &extensions);
+    ShaderD3D(const gl::ShaderState &state, RendererD3D *renderer);
     ~ShaderD3D() override;
 
     std::shared_ptr<WaitableCompileEvent> compile(const gl::Context *context,
                                                   gl::ShCompilerInstance *compilerInstance,
-                                                  ShCompileOptions options) override;
+                                                  ShCompileOptions *options) override;
 
     std::string getDebugInfo() const override;
 
@@ -111,6 +109,7 @@
     bool mUsesNestedBreak;
     bool mRequiresIEEEStrictCompiling;
 
+    RendererD3D *mRenderer;
     ShShaderOutput mCompilerOutputType;
     mutable std::string mDebugInfo;
     std::map<std::string, unsigned int> mUniformRegisterMap;
@@ -121,7 +120,6 @@
     unsigned int mReadonlyImage2DRegisterIndex;
     unsigned int mImage2DRegisterIndex;
     std::set<std::string> mUsedImage2DFunctionNames;
-    ShCompileOptions mAdditionalOptions;
 };
 }  // namespace rx
 
diff --git a/src/libANGLE/renderer/d3d/d3d11/Context11.cpp b/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
index 00b4f4b..369fc99 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
@@ -144,7 +144,7 @@
 
 ShaderImpl *Context11::createShader(const gl::ShaderState &data)
 {
-    return new ShaderD3D(data, mRenderer->getFeatures(), mRenderer->getNativeExtensions());
+    return new ShaderD3D(data, mRenderer);
 }
 
 ProgramImpl *Context11::createProgram(const gl::ProgramState &data)
diff --git a/src/libANGLE/renderer/d3d/d3d9/Context9.cpp b/src/libANGLE/renderer/d3d/d3d9/Context9.cpp
index 1bbb5dc..a4981cc 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Context9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/Context9.cpp
@@ -52,7 +52,7 @@
 
 ShaderImpl *Context9::createShader(const gl::ShaderState &data)
 {
-    return new ShaderD3D(data, mRenderer->getFeatures(), mRenderer->getNativeExtensions());
+    return new ShaderD3D(data, mRenderer);
 }
 
 ProgramImpl *Context9::createProgram(const gl::ProgramState &data)
diff --git a/src/libANGLE/renderer/gl/CompilerGL.cpp b/src/libANGLE/renderer/gl/CompilerGL.cpp
index ee4b27e..5cf797d 100644
--- a/src/libANGLE/renderer/gl/CompilerGL.cpp
+++ b/src/libANGLE/renderer/gl/CompilerGL.cpp
@@ -16,114 +16,9 @@
 
 namespace rx
 {
-
-namespace
-{
-
-ShShaderOutput GetShaderOutputType(const FunctionsGL *functions)
-{
-    ASSERT(functions);
-
-    if (functions->standard == STANDARD_GL_DESKTOP)
-    {
-        // GLSL outputs
-        if (functions->isAtLeastGL(gl::Version(4, 5)))
-        {
-            return SH_GLSL_450_CORE_OUTPUT;
-        }
-        else if (functions->isAtLeastGL(gl::Version(4, 4)))
-        {
-            return SH_GLSL_440_CORE_OUTPUT;
-        }
-        else if (functions->isAtLeastGL(gl::Version(4, 3)))
-        {
-            return SH_GLSL_430_CORE_OUTPUT;
-        }
-        else if (functions->isAtLeastGL(gl::Version(4, 2)))
-        {
-            return SH_GLSL_420_CORE_OUTPUT;
-        }
-        else if (functions->isAtLeastGL(gl::Version(4, 1)))
-        {
-            return SH_GLSL_410_CORE_OUTPUT;
-        }
-        else if (functions->isAtLeastGL(gl::Version(4, 0)))
-        {
-            return SH_GLSL_400_CORE_OUTPUT;
-        }
-        else if (functions->isAtLeastGL(gl::Version(3, 3)))
-        {
-            return SH_GLSL_330_CORE_OUTPUT;
-        }
-        else if (functions->isAtLeastGL(gl::Version(3, 2)))
-        {
-            return SH_GLSL_150_CORE_OUTPUT;
-        }
-        else if (functions->isAtLeastGL(gl::Version(3, 1)))
-        {
-            return SH_GLSL_140_OUTPUT;
-        }
-        else if (functions->isAtLeastGL(gl::Version(3, 0)))
-        {
-            return SH_GLSL_130_OUTPUT;
-        }
-        else
-        {
-            return SH_GLSL_COMPATIBILITY_OUTPUT;
-        }
-    }
-    else if (functions->standard == STANDARD_GL_ES)
-    {
-        // ESSL outputs
-        return SH_ESSL_OUTPUT;
-    }
-    else
-    {
-        UNREACHABLE();
-        return ShShaderOutput(0);
-    }
-}
-
-}  // anonymous namespace
-
 CompilerGL::CompilerGL(const ContextGL *context)
     : mTranslatorOutputType(GetShaderOutputType(context->getFunctions()))
-{
-    if (context->getExtensions().shaderPixelLocalStorageCoherentANGLE)
-    {
-        const angle::FeaturesGL &features = context->getFeaturesGL();
-        // Prefer vendor-specific extensions first. The PixelLocalStorageTest.Coherency test doesn't
-        // always pass on Intel when we use the ARB extension.
-        if (features.supportsFragmentShaderInterlockNV.enabled)
-        {
-            // This extension requires 430+. GetShaderOutputType() should always select 430+ on a GL
-            // 4.3 context, where this extension is defined.
-            ASSERT(context->getFunctions()->isAtLeastGL(gl::Version(4, 3)));
-            ASSERT(mTranslatorOutputType >= SH_GLSL_430_CORE_OUTPUT);
-            mBackendFeatures.fragmentSynchronizationType =
-                ShFragmentSynchronizationType::FragmentShaderInterlock_NV_GL;
-        }
-        else if (features.supportsFragmentShaderOrderingINTEL.enabled)
-        {
-            // This extension requires 440+. GetShaderOutputType() should always select 440+ on a GL
-            // 4.4 context, where this extension is defined.
-            ASSERT(context->getFunctions()->isAtLeastGL(gl::Version(4, 4)));
-            ASSERT(mTranslatorOutputType >= SH_GLSL_440_CORE_OUTPUT);
-            mBackendFeatures.fragmentSynchronizationType =
-                ShFragmentSynchronizationType::FragmentShaderOrdering_INTEL_GL;
-        }
-        else
-        {
-            ASSERT(features.supportsFragmentShaderInterlockARB.enabled);
-            // This extension requires 450+. GetShaderOutputType() should always select 450+ on a GL
-            // 4.5 context, where this extension is defined.
-            ASSERT(context->getFunctions()->isAtLeastGL(gl::Version(4, 5)));
-            ASSERT(mTranslatorOutputType >= SH_GLSL_450_CORE_OUTPUT);
-            mBackendFeatures.fragmentSynchronizationType =
-                ShFragmentSynchronizationType::FragmentShaderInterlock_ARB_GL;
-        }
-    }
-}
+{}
 
 ShShaderOutput CompilerGL::getTranslatorOutputType() const
 {
diff --git a/src/libANGLE/renderer/gl/CompilerGL.h b/src/libANGLE/renderer/gl/CompilerGL.h
index 6aa89ec..78c3850 100644
--- a/src/libANGLE/renderer/gl/CompilerGL.h
+++ b/src/libANGLE/renderer/gl/CompilerGL.h
@@ -23,11 +23,9 @@
     ~CompilerGL() override {}
 
     ShShaderOutput getTranslatorOutputType() const override;
-    CompilerBackendFeatures getBackendFeatures() const override { return mBackendFeatures; }
 
   private:
     ShShaderOutput mTranslatorOutputType;
-    CompilerBackendFeatures mBackendFeatures;
 };
 
 }  // namespace rx
diff --git a/src/libANGLE/renderer/gl/ShaderGL.cpp b/src/libANGLE/renderer/gl/ShaderGL.cpp
index 839e181..0f21172 100644
--- a/src/libANGLE/renderer/gl/ShaderGL.cpp
+++ b/src/libANGLE/renderer/gl/ShaderGL.cpp
@@ -26,7 +26,7 @@
 {
   public:
     TranslateTaskGL(ShHandle handle,
-                    ShCompileOptions options,
+                    const ShCompileOptions &options,
                     const std::string &source,
                     CompileAndCheckShaderInWorkerFunctor &&compileAndCheckShaderInWorkerFunctor)
         : mHandle(handle),
@@ -240,137 +240,171 @@
 
 std::shared_ptr<WaitableCompileEvent> ShaderGL::compile(const gl::Context *context,
                                                         gl::ShCompilerInstance *compilerInstance,
-                                                        ShCompileOptions options)
+                                                        ShCompileOptions *options)
 {
     mInfoLog.clear();
 
-    ShCompileOptions additionalOptions = SH_INIT_GL_POSITION;
+    options->initGLPosition = true;
 
     bool isWebGL = context->isWebGL();
     if (isWebGL && mState.getShaderType() != gl::ShaderType::Compute)
     {
-        additionalOptions |= SH_INIT_OUTPUT_VARIABLES;
+        options->initOutputVariables = true;
     }
 
     if (isWebGL && !context->getState().getEnableFeature(GL_TEXTURE_RECTANGLE_ANGLE))
     {
-        additionalOptions |= SH_DISABLE_ARB_TEXTURE_RECTANGLE;
+        options->disableARBTextureRectangle = true;
     }
 
     const angle::FeaturesGL &features = GetFeaturesGL(context);
 
     if (features.initFragmentOutputVariables.enabled)
     {
-        additionalOptions |= SH_INIT_FRAGMENT_OUTPUT_VARIABLES;
+        options->initFragmentOutputVariables = true;
     }
 
     if (features.doWhileGLSLCausesGPUHang.enabled)
     {
-        additionalOptions |= SH_REWRITE_DO_WHILE_LOOPS;
+        options->rewriteDoWhileLoops = true;
     }
 
     if (features.emulateAbsIntFunction.enabled)
     {
-        additionalOptions |= SH_EMULATE_ABS_INT_FUNCTION;
+        options->emulateAbsIntFunction = true;
     }
 
     if (features.addAndTrueToLoopCondition.enabled)
     {
-        additionalOptions |= SH_ADD_AND_TRUE_TO_LOOP_CONDITION;
+        options->addAndTrueToLoopCondition = true;
     }
 
     if (features.emulateIsnanFloat.enabled)
     {
-        additionalOptions |= SH_EMULATE_ISNAN_FLOAT_FUNCTION;
+        options->emulateIsnanFloatFunction = true;
     }
 
     if (features.emulateAtan2Float.enabled)
     {
-        additionalOptions |= SH_EMULATE_ATAN2_FLOAT_FUNCTION;
+        options->emulateAtan2FloatFunction = true;
     }
 
     if (features.useUnusedBlocksWithStandardOrSharedLayout.enabled)
     {
-        additionalOptions |= SH_USE_UNUSED_STANDARD_SHARED_BLOCKS;
+        options->useUnusedStandardSharedBlocks = true;
     }
 
     if (features.removeInvariantAndCentroidForESSL3.enabled)
     {
-        additionalOptions |= SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3;
+        options->removeInvariantAndCentroidForESSL3 = true;
     }
 
     if (features.rewriteFloatUnaryMinusOperator.enabled)
     {
-        additionalOptions |= SH_REWRITE_FLOAT_UNARY_MINUS_OPERATOR;
+        options->rewriteFloatUnaryMinusOperator = true;
     }
 
     if (!features.dontInitializeUninitializedLocals.enabled)
     {
-        additionalOptions |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
+        options->initializeUninitializedLocals = true;
     }
 
     if (features.clampPointSize.enabled)
     {
-        additionalOptions |= SH_CLAMP_POINT_SIZE;
+        options->clampPointSize = true;
     }
 
     if (features.dontUseLoopsToInitializeVariables.enabled)
     {
-        additionalOptions |= SH_DONT_USE_LOOPS_TO_INITIALIZE_VARIABLES;
+        options->dontUseLoopsToInitializeVariables = true;
     }
 
     if (features.clampFragDepth.enabled)
     {
-        additionalOptions |= SH_CLAMP_FRAG_DEPTH;
+        options->clampFragDepth = true;
     }
 
     if (features.rewriteRepeatedAssignToSwizzled.enabled)
     {
-        additionalOptions |= SH_REWRITE_REPEATED_ASSIGN_TO_SWIZZLED;
+        options->rewriteRepeatedAssignToSwizzled = true;
     }
 
     if (mMultiviewImplementationType == MultiviewImplementationTypeGL::NV_VIEWPORT_ARRAY2)
     {
-        additionalOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
-        additionalOptions |= SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER;
+        options->initializeBuiltinsForInstancedMultiview = true;
+        options->selectViewInNvGLSLVertexShader          = true;
     }
 
     if (features.clampArrayAccess.enabled || isWebGL)
     {
-        additionalOptions |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
+        options->clampIndirectArrayBounds = true;
     }
 
     if (features.vertexIDDoesNotIncludeBaseVertex.enabled)
     {
-        additionalOptions |= SH_ADD_BASE_VERTEX_TO_VERTEX_ID;
+        options->addBaseVertexToVertexID = true;
     }
 
     if (features.unfoldShortCircuits.enabled)
     {
-        additionalOptions |= SH_UNFOLD_SHORT_CIRCUIT;
+        options->unfoldShortCircuit = true;
     }
 
     if (features.removeDynamicIndexingOfSwizzledVector.enabled)
     {
-        additionalOptions |= SH_REMOVE_DYNAMIC_INDEXING_OF_SWIZZLED_VECTOR;
+        options->removeDynamicIndexingOfSwizzledVector = true;
     }
 
     if (features.preAddTexelFetchOffsets.enabled)
     {
-        additionalOptions |= SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH;
+        options->rewriteTexelFetchOffsetToTexelFetch = true;
     }
 
     if (features.regenerateStructNames.enabled)
     {
-        additionalOptions |= SH_REGENERATE_STRUCT_NAMES;
+        options->regenerateStructNames = true;
     }
 
     if (features.rewriteRowMajorMatrices.enabled)
     {
-        additionalOptions |= SH_REWRITE_ROW_MAJOR_MATRICES;
+        options->rewriteRowMajorMatrices = true;
     }
 
-    options |= additionalOptions;
+    if (mRenderer->getNativeExtensions().shaderPixelLocalStorageCoherentANGLE)
+    {
+        const ShShaderOutput translatorOutputType = GetShaderOutputType(GetFunctionsGL(context));
+
+        // Prefer vendor-specific extensions first. The PixelLocalStorageTest.Coherency test doesn't
+        // always pass on Intel when we use the ARB extension.
+        if (features.supportsFragmentShaderInterlockNV.enabled)
+        {
+            // This extension requires 430+. GetShaderOutputType() should always select 430+ on a GL
+            // 4.3 context, where this extension is defined.
+            ASSERT(mRenderer->getFunctions()->isAtLeastGL(gl::Version(4, 3)));
+            ASSERT(translatorOutputType >= SH_GLSL_430_CORE_OUTPUT);
+            options->pls.fragmentSynchronizationType =
+                ShFragmentSynchronizationType::FragmentShaderInterlock_NV_GL;
+        }
+        else if (features.supportsFragmentShaderOrderingINTEL.enabled)
+        {
+            // This extension requires 440+. GetShaderOutputType() should always select 440+ on a GL
+            // 4.4 context, where this extension is defined.
+            ASSERT(mRenderer->getFunctions()->isAtLeastGL(gl::Version(4, 4)));
+            ASSERT(translatorOutputType >= SH_GLSL_440_CORE_OUTPUT);
+            options->pls.fragmentSynchronizationType =
+                ShFragmentSynchronizationType::FragmentShaderOrdering_INTEL_GL;
+        }
+        else
+        {
+            ASSERT(features.supportsFragmentShaderInterlockARB.enabled);
+            // This extension requires 450+. GetShaderOutputType() should always select 450+ on a GL
+            // 4.5 context, where this extension is defined.
+            ASSERT(mRenderer->getFunctions()->isAtLeastGL(gl::Version(4, 5)));
+            ASSERT(translatorOutputType >= SH_GLSL_450_CORE_OUTPUT);
+            options->pls.fragmentSynchronizationType =
+                ShFragmentSynchronizationType::FragmentShaderInterlock_ARB_GL;
+        }
+    }
 
     auto workerThreadPool = context->getShaderCompileThreadPool();
 
@@ -389,7 +423,7 @@
     {
         ShHandle handle = compilerInstance->getHandle();
         const char *str = source.c_str();
-        bool result     = sh::Compile(handle, &str, 1, options);
+        bool result     = sh::Compile(handle, &str, 1, *options);
         if (result)
         {
             compileShader(sh::GetObjectCode(handle).c_str());
@@ -411,7 +445,7 @@
             return compileAndCheckShaderInWorker(source);
         };
         auto translateTask =
-            std::make_shared<TranslateTaskGL>(compilerInstance->getHandle(), options, source,
+            std::make_shared<TranslateTaskGL>(compilerInstance->getHandle(), *options, source,
                                               std::move(compileAndCheckShaderInWorkerFunctor));
 
         auto compileAndCheckShaderFunctor = [this](const char *source) {
@@ -426,7 +460,7 @@
     {
         ShHandle handle = compilerInstance->getHandle();
         const char *str = source.c_str();
-        bool result     = sh::Compile(handle, &str, 1, options);
+        bool result     = sh::Compile(handle, &str, 1, *options);
         if (result)
         {
             compileAndCheckShader(sh::GetObjectCode(handle).c_str());
diff --git a/src/libANGLE/renderer/gl/ShaderGL.h b/src/libANGLE/renderer/gl/ShaderGL.h
index b541e32..f7180df 100644
--- a/src/libANGLE/renderer/gl/ShaderGL.h
+++ b/src/libANGLE/renderer/gl/ShaderGL.h
@@ -29,7 +29,7 @@
 
     std::shared_ptr<WaitableCompileEvent> compile(const gl::Context *context,
                                                   gl::ShCompilerInstance *compilerInstance,
-                                                  ShCompileOptions options) override;
+                                                  ShCompileOptions *options) override;
 
     std::string getDebugInfo() const override;
 
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index a822af7..cc99120 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -259,6 +259,70 @@
     return 0;
 }
 
+ShShaderOutput GetShaderOutputType(const FunctionsGL *functions)
+{
+    ASSERT(functions);
+
+    if (functions->standard == STANDARD_GL_DESKTOP)
+    {
+        // GLSL outputs
+        if (functions->isAtLeastGL(gl::Version(4, 5)))
+        {
+            return SH_GLSL_450_CORE_OUTPUT;
+        }
+        else if (functions->isAtLeastGL(gl::Version(4, 4)))
+        {
+            return SH_GLSL_440_CORE_OUTPUT;
+        }
+        else if (functions->isAtLeastGL(gl::Version(4, 3)))
+        {
+            return SH_GLSL_430_CORE_OUTPUT;
+        }
+        else if (functions->isAtLeastGL(gl::Version(4, 2)))
+        {
+            return SH_GLSL_420_CORE_OUTPUT;
+        }
+        else if (functions->isAtLeastGL(gl::Version(4, 1)))
+        {
+            return SH_GLSL_410_CORE_OUTPUT;
+        }
+        else if (functions->isAtLeastGL(gl::Version(4, 0)))
+        {
+            return SH_GLSL_400_CORE_OUTPUT;
+        }
+        else if (functions->isAtLeastGL(gl::Version(3, 3)))
+        {
+            return SH_GLSL_330_CORE_OUTPUT;
+        }
+        else if (functions->isAtLeastGL(gl::Version(3, 2)))
+        {
+            return SH_GLSL_150_CORE_OUTPUT;
+        }
+        else if (functions->isAtLeastGL(gl::Version(3, 1)))
+        {
+            return SH_GLSL_140_OUTPUT;
+        }
+        else if (functions->isAtLeastGL(gl::Version(3, 0)))
+        {
+            return SH_GLSL_130_OUTPUT;
+        }
+        else
+        {
+            return SH_GLSL_COMPATIBILITY_OUTPUT;
+        }
+    }
+    else if (functions->standard == STANDARD_GL_ES)
+    {
+        // ESSL outputs
+        return SH_ESSL_OUTPUT;
+    }
+    else
+    {
+        UNREACHABLE();
+        return ShShaderOutput(0);
+    }
+}
+
 namespace nativegl_gl
 {
 
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.h b/src/libANGLE/renderer/gl/renderergl_utils.h
index 3e5ac06..810f00f 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.h
+++ b/src/libANGLE/renderer/gl/renderergl_utils.h
@@ -63,6 +63,7 @@
 };
 
 VendorID GetVendorID(const FunctionsGL *functions);
+ShShaderOutput GetShaderOutputType(const FunctionsGL *functions);
 
 // Helpers for extracting the GL helper objects out of a context
 const FunctionsGL *GetFunctionsGL(const gl::Context *context);
diff --git a/src/libANGLE/renderer/metal/DisplayMtl.mm b/src/libANGLE/renderer/metal/DisplayMtl.mm
index ac66548..7a42a4d 100644
--- a/src/libANGLE/renderer/metal/DisplayMtl.mm
+++ b/src/libANGLE/renderer/metal/DisplayMtl.mm
@@ -884,11 +884,6 @@
     {
         mNativeLimitations.noCompressedTexture3D = true;
     }
-
-    // Direct-to-metal constants:
-    mNativeCaps.driverUniformsBindingIndex    = mtl::kDriverUniformsBindingIndex;
-    mNativeCaps.defaultUniformsBindingIndex   = mtl::kDefaultUniformsBindingIndex;
-    mNativeCaps.UBOArgumentBufferBindingIndex = mtl::kUBOArgumentBufferBindingIndex;
 }
 
 void DisplayMtl::initializeExtensions() const
diff --git a/src/libANGLE/renderer/metal/ShaderMtl.h b/src/libANGLE/renderer/metal/ShaderMtl.h
index f9b2d85..7228362 100644
--- a/src/libANGLE/renderer/metal/ShaderMtl.h
+++ b/src/libANGLE/renderer/metal/ShaderMtl.h
@@ -24,7 +24,7 @@
 
     std::shared_ptr<WaitableCompileEvent> compile(const gl::Context *context,
                                                   gl::ShCompilerInstance *compilerInstance,
-                                                  ShCompileOptions options) override;
+                                                  ShCompileOptions *options) override;
 
     sh::TranslatorMetalReflection *getTranslatorMetalReflection()
     {
@@ -39,7 +39,7 @@
     std::shared_ptr<WaitableCompileEvent> compileImplMtl(const gl::Context *context,
                                                          gl::ShCompilerInstance *compilerInstance,
                                                          const std::string &source,
-                                                         ShCompileOptions compileOptions);
+                                                         ShCompileOptions *compileOptions);
 };
 
 }  // namespace rx
diff --git a/src/libANGLE/renderer/metal/ShaderMtl.mm b/src/libANGLE/renderer/metal/ShaderMtl.mm
index 40be0c6..9733bad 100644
--- a/src/libANGLE/renderer/metal/ShaderMtl.mm
+++ b/src/libANGLE/renderer/metal/ShaderMtl.mm
@@ -27,7 +27,7 @@
 class TranslateTask : public angle::Closure
 {
   public:
-    TranslateTask(ShHandle handle, ShCompileOptions options, const std::string &source)
+    TranslateTask(ShHandle handle, const ShCompileOptions &options, const std::string &source)
         : mHandle(handle), mOptions(options), mSource(source), mResult(false)
     {}
 
@@ -82,17 +82,17 @@
     const gl::Context *context,
     gl::ShCompilerInstance *compilerInstance,
     const std::string &source,
-    ShCompileOptions compileOptions)
+    ShCompileOptions *compileOptions)
 {
 // TODO(jcunningham): Remove this workaround once correct fix to move validation to the very end is
 // in place. See: https://bugs.webkit.org/show_bug.cgi?id=224991
 #if defined(ANGLE_ENABLE_ASSERTS) && 0
-    compileOptions |= SH_VALIDATE_AST;
+    compileOptions->validateAst = true;
 #endif
 
     auto workerThreadPool = context->getWorkerThreadPool();
     auto translateTask =
-        std::make_shared<TranslateTask>(compilerInstance->getHandle(), compileOptions, source);
+        std::make_shared<TranslateTask>(compilerInstance->getHandle(), *compileOptions, source);
 
     return std::make_shared<MTLWaitableCompileEventImpl>(
         this, angle::WorkerThreadPool::PostWorkerTask(workerThreadPool, translateTask),
@@ -101,39 +101,43 @@
 
 std::shared_ptr<WaitableCompileEvent> ShaderMtl::compile(const gl::Context *context,
                                                          gl::ShCompilerInstance *compilerInstance,
-                                                         ShCompileOptions options)
+                                                         ShCompileOptions *options)
 {
-    ContextMtl *contextMtl          = mtl::GetImpl(context);
-    ShCompileOptions compileOptions = SH_INITIALIZE_UNINITIALIZED_LOCALS;
+    ContextMtl *contextMtl                 = mtl::GetImpl(context);
+    options->initializeUninitializedLocals = true;
 
     if (context->isWebGL() && mState.getShaderType() != gl::ShaderType::Compute)
     {
-        compileOptions |= SH_INIT_OUTPUT_VARIABLES;
+        options->initOutputVariables = true;
     }
 
     if (contextMtl->getDisplay()->getFeatures().intelExplicitBoolCastWorkaround.enabled)
     {
-        compileOptions |= SH_ADD_EXPLICIT_BOOL_CASTS;
+        options->addExplicitBoolCasts = true;
     }
 
-    compileOptions |= SH_CLAMP_POINT_SIZE;
+    options->clampPointSize = true;
 #if defined(ANGLE_PLATFORM_IOS) && !defined(ANGLE_PLATFORM_MACCATALYST)
-    compileOptions |= SH_CLAMP_FRAG_DEPTH;
+    options->clampFragDepth = true;
 #endif
 
     if (contextMtl->getDisplay()->getFeatures().rewriteRowMajorMatrices.enabled)
     {
-        compileOptions |= SH_REWRITE_ROW_MAJOR_MATRICES;
+        options->rewriteRowMajorMatrices = true;
     }
     // If compiling through SPIR-V
-    compileOptions |= SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE;
-    compileOptions |= SH_ADD_VULKAN_DEPTH_CORRECTION;
+    options->addVulkanXfbEmulationSupportCode = true;
+    options->addVulkanDepthCorrection         = true;
     // If compiling through SPIR-V.  This path outputs text, so cannot use the direct SPIR-V gen
     // path unless fixed.
-    compileOptions |= SH_GENERATE_SPIRV_THROUGH_GLSLANG;
+    options->generateSpirvThroughGlslang = true;
 
-    return compileImplMtl(context, compilerInstance, getState().getSource(),
-                          compileOptions | options);
+    // Direct-to-metal constants:
+    options->metal.driverUniformsBindingIndex    = mtl::kDriverUniformsBindingIndex;
+    options->metal.defaultUniformsBindingIndex   = mtl::kDefaultUniformsBindingIndex;
+    options->metal.UBOArgumentBufferBindingIndex = mtl::kUBOArgumentBufferBindingIndex;
+
+    return compileImplMtl(context, compilerInstance, getState().getSource(), options);
 }
 
 std::string ShaderMtl::getDebugInfo() const
diff --git a/src/libANGLE/renderer/null/ShaderNULL.cpp b/src/libANGLE/renderer/null/ShaderNULL.cpp
index e398d67..5e81afb 100644
--- a/src/libANGLE/renderer/null/ShaderNULL.cpp
+++ b/src/libANGLE/renderer/null/ShaderNULL.cpp
@@ -21,7 +21,7 @@
 
 std::shared_ptr<WaitableCompileEvent> ShaderNULL::compile(const gl::Context *context,
                                                           gl::ShCompilerInstance *compilerInstance,
-                                                          ShCompileOptions options)
+                                                          ShCompileOptions *options)
 {
     return compileImpl(context, compilerInstance, mState.getSource(), options);
 }
diff --git a/src/libANGLE/renderer/null/ShaderNULL.h b/src/libANGLE/renderer/null/ShaderNULL.h
index 8efab1a..b51ef79 100644
--- a/src/libANGLE/renderer/null/ShaderNULL.h
+++ b/src/libANGLE/renderer/null/ShaderNULL.h
@@ -23,7 +23,7 @@
 
     std::shared_ptr<WaitableCompileEvent> compile(const gl::Context *context,
                                                   gl::ShCompilerInstance *compilerInstance,
-                                                  ShCompileOptions options) override;
+                                                  ShCompileOptions *options) override;
 
     std::string getDebugInfo() const override;
 };
diff --git a/src/libANGLE/renderer/vulkan/ShaderVk.cpp b/src/libANGLE/renderer/vulkan/ShaderVk.cpp
index 826b4c1..0ff1f46 100644
--- a/src/libANGLE/renderer/vulkan/ShaderVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ShaderVk.cpp
@@ -23,90 +23,88 @@
 
 std::shared_ptr<WaitableCompileEvent> ShaderVk::compile(const gl::Context *context,
                                                         gl::ShCompilerInstance *compilerInstance,
-                                                        ShCompileOptions options)
+                                                        ShCompileOptions *options)
 {
-    ShCompileOptions compileOptions = 0;
-
     ContextVk *contextVk = vk::GetImpl(context);
 
     if (context->isWebGL())
     {
         // Only WebGL requires initialization of local variables, others don't.
         // Extra initialization in spirv shader may affect performance.
-        compileOptions |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
+        options->initializeUninitializedLocals = true;
 
         // WebGL shaders may contain OOB array accesses which in turn cause undefined behavior,
         // which may result in security issues. See https://crbug.com/1189110.
-        compileOptions |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
+        options->clampIndirectArrayBounds = true;
 
         if (mState.getShaderType() != gl::ShaderType::Compute)
         {
-            compileOptions |= SH_INIT_OUTPUT_VARIABLES;
+            options->initOutputVariables = true;
         }
     }
 
     if (contextVk->getFeatures().clampPointSize.enabled)
     {
-        compileOptions |= SH_CLAMP_POINT_SIZE;
+        options->clampPointSize = true;
     }
 
     if (contextVk->getFeatures().emulateAdvancedBlendEquations.enabled)
     {
-        compileOptions |= SH_ADD_ADVANCED_BLEND_EQUATIONS_EMULATION;
+        options->addAdvancedBlendEquationsEmulation = true;
     }
 
     if (contextVk->emulateSeamfulCubeMapSampling())
     {
-        compileOptions |= SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING;
+        options->emulateSeamfulCubeMapSampling = true;
     }
 
     if (!contextVk->getFeatures().enablePrecisionQualifiers.enabled)
     {
-        compileOptions |= SH_IGNORE_PRECISION_QUALIFIERS;
+        options->ignorePrecisionQualifiers = true;
     }
 
     if (contextVk->getFeatures().forceFragmentShaderPrecisionHighpToMediump.enabled)
     {
-        compileOptions |= SH_FORCE_SHADER_PRECISION_HIGHP_TO_MEDIUMP;
+        options->forceShaderPrecisionHighpToMediump = true;
     }
 
     // Let compiler use specialized constant for pre-rotation.
     if (!contextVk->getFeatures().preferDriverUniformOverSpecConst.enabled)
     {
-        compileOptions |= SH_USE_SPECIALIZATION_CONSTANT;
+        options->useSpecializationConstant = true;
     }
 
     if (!contextVk->getFeatures().supportsDepthClipControl.enabled)
     {
-        compileOptions |= SH_ADD_VULKAN_DEPTH_CORRECTION;
+        options->addVulkanDepthCorrection = true;
     }
 
     if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
     {
-        compileOptions |= SH_ADD_VULKAN_XFB_EXTENSION_SUPPORT_CODE;
+        options->addVulkanXfbExtensionSupportCode = true;
     }
     else if (mState.getShaderType() == gl::ShaderType::Vertex &&
              contextVk->getFeatures().emulateTransformFeedback.enabled)
     {
-        compileOptions |= SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE;
+        options->addVulkanXfbEmulationSupportCode = true;
     }
 
     if (contextVk->getFeatures().generateSPIRVThroughGlslang.enabled)
     {
-        compileOptions |= SH_GENERATE_SPIRV_THROUGH_GLSLANG;
+        options->generateSpirvThroughGlslang = true;
     }
 
     if (contextVk->getFeatures().roundOutputAfterDithering.enabled)
     {
-        compileOptions |= SH_ROUND_OUTPUT_AFTER_DITHERING;
+        options->roundOutputAfterDithering = true;
     }
 
     if (contextVk->getFeatures().precisionSafeDivision.enabled)
     {
-        compileOptions |= SH_PRECISION_SAFE_DIVISION;
+        options->precisionSafeDivision = true;
     }
 
-    return compileImpl(context, compilerInstance, mState.getSource(), compileOptions | options);
+    return compileImpl(context, compilerInstance, mState.getSource(), options);
 }
 
 std::string ShaderVk::getDebugInfo() const
diff --git a/src/libANGLE/renderer/vulkan/ShaderVk.h b/src/libANGLE/renderer/vulkan/ShaderVk.h
index d76a862..7413321 100644
--- a/src/libANGLE/renderer/vulkan/ShaderVk.h
+++ b/src/libANGLE/renderer/vulkan/ShaderVk.h
@@ -23,7 +23,7 @@
 
     std::shared_ptr<WaitableCompileEvent> compile(const gl::Context *context,
                                                   gl::ShCompilerInstance *compilerInstance,
-                                                  ShCompileOptions options) override;
+                                                  ShCompileOptions *options) override;
 
     std::string getDebugInfo() const override;
 };
diff --git a/src/tests/compiler_tests/APPLE_clip_distance_test.cpp b/src/tests/compiler_tests/APPLE_clip_distance_test.cpp
index 2d1a818..563480d 100644
--- a/src/tests/compiler_tests/APPLE_clip_distance_test.cpp
+++ b/src/tests/compiler_tests/APPLE_clip_distance_test.cpp
@@ -59,7 +59,12 @@
     {
         const char *shaderStrings[] = {testing::get<1>(GetParam()), pragma,
                                        testing::get<2>(GetParam())};
-        bool success = sh::Compile(mCompiler, shaderStrings, 3, SH_VARIABLES | SH_OBJECT_CODE);
+
+        ShCompileOptions compileOptions = {};
+        compileOptions.objectCode       = true;
+        compileOptions.variables        = true;
+
+        bool success = sh::Compile(mCompiler, shaderStrings, 3, compileOptions);
         if (success)
         {
             return ::testing::AssertionSuccess() << "Compilation success";
@@ -126,7 +131,10 @@
     })";
     const char *shaderStrings[]  = {kNoClipCull};
 
-    bool success = sh::Compile(mCompiler, shaderStrings, 1, SH_OBJECT_CODE);
+    ShCompileOptions compileOptions = {};
+    compileOptions.objectCode       = true;
+
+    bool success = sh::Compile(mCompiler, shaderStrings, 1, compileOptions);
     if (success)
     {
         ::testing::AssertionSuccess() << "Compilation success";
diff --git a/src/tests/compiler_tests/ARB_texture_rectangle_test.cpp b/src/tests/compiler_tests/ARB_texture_rectangle_test.cpp
index f2e496b..93669e1 100644
--- a/src/tests/compiler_tests/ARB_texture_rectangle_test.cpp
+++ b/src/tests/compiler_tests/ARB_texture_rectangle_test.cpp
@@ -165,7 +165,7 @@
         precision mediump float;
         uniform sampler2DRect s;
         void main() {})";
-    mExtraCompileOptions |= SH_DISABLE_ARB_TEXTURE_RECTANGLE;
+    mCompileOptions.disableARBTextureRectangle = true;
     if (compile(shaderString))
     {
         FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
@@ -184,12 +184,12 @@
     {
         FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
     }
-    mExtraCompileOptions |= SH_DISABLE_ARB_TEXTURE_RECTANGLE;
+    mCompileOptions.disableARBTextureRectangle = true;
     if (compile(shaderString))
     {
         FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
     }
-    mExtraCompileOptions &= ~SH_DISABLE_ARB_TEXTURE_RECTANGLE;
+    mCompileOptions.disableARBTextureRectangle = false;
     if (!compile(shaderString))
     {
         FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
diff --git a/src/tests/compiler_tests/AtomicCounter_test.cpp b/src/tests/compiler_tests/AtomicCounter_test.cpp
index 3cfa0af..d914881 100644
--- a/src/tests/compiler_tests/AtomicCounter_test.cpp
+++ b/src/tests/compiler_tests/AtomicCounter_test.cpp
@@ -34,7 +34,7 @@
 // and the values of offset are properly assigned to counter variables.
 TEST_F(AtomicCounterTest, BasicAtomicCounterDeclaration)
 {
-    mExtraCompileOptions |= SH_VARIABLES;
+    mCompileOptions.variables = true;
     const std::string &source =
         "#version 310 es\n"
         "layout(binding = 2, offset = 4) uniform atomic_uint a;\n"
diff --git a/src/tests/compiler_tests/BufferVariables_test.cpp b/src/tests/compiler_tests/BufferVariables_test.cpp
index 3a83de5..f9b533d 100644
--- a/src/tests/compiler_tests/BufferVariables_test.cpp
+++ b/src/tests/compiler_tests/BufferVariables_test.cpp
@@ -34,7 +34,7 @@
 class BufferVariablesMatchTest : public MatchOutputCodeTest
 {
   public:
-    BufferVariablesMatchTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, 0, SH_ESSL_OUTPUT)
+    BufferVariablesMatchTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_ESSL_OUTPUT)
     {
         getResources()->MaxShaderStorageBufferBindings = 8;
     }
diff --git a/src/tests/compiler_tests/CollectVariables_test.cpp b/src/tests/compiler_tests/CollectVariables_test.cpp
index 240c012..e0d8960 100644
--- a/src/tests/compiler_tests/CollectVariables_test.cpp
+++ b/src/tests/compiler_tests/CollectVariables_test.cpp
@@ -56,8 +56,10 @@
     // For use in the gl_DepthRange tests.
     void validateDepthRangeShader(const std::string &shaderString)
     {
-        const char *shaderStrings[] = {shaderString.c_str()};
-        ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES));
+        const char *shaderStrings[]     = {shaderString.c_str()};
+        ShCompileOptions compileOptions = {};
+        compileOptions.variables        = true;
+        ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, compileOptions));
 
         const std::vector<ShaderVariable> &uniforms = mTranslator->getUniforms();
         ASSERT_EQ(1u, uniforms.size());
@@ -106,8 +108,10 @@
                                          const char *varName,
                                          const ShaderVariable **outResult)
     {
-        const char *shaderStrings[] = {shaderString.c_str()};
-        ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES))
+        const char *shaderStrings[]     = {shaderString.c_str()};
+        ShCompileOptions compileOptions = {};
+        compileOptions.variables        = true;
+        ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, compileOptions))
             << mTranslator->getInfoSink().info.str();
 
         const auto &outputVariables = mTranslator->getOutputVariables();
@@ -120,13 +124,19 @@
         *outResult = &outputVariable;
     }
 
-    void compile(const std::string &shaderString, ShCompileOptions compileOptions)
+    void compile(const std::string &shaderString, ShCompileOptions *compileOptions)
     {
+        compileOptions->variables = true;
+
         const char *shaderStrings[] = {shaderString.c_str()};
-        ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES | compileOptions));
+        ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, *compileOptions));
     }
 
-    void compile(const std::string &shaderString) { compile(shaderString, 0u); }
+    void compile(const std::string &shaderString)
+    {
+        ShCompileOptions options = {};
+        compile(shaderString, &options);
+    }
 
     void checkUniformStaticallyUsedButNotActive(const char *name)
     {
@@ -1033,8 +1043,10 @@
     resources.MaxViewsOVR        = 4;
     initTranslator(resources);
 
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    ShCompileOptions compileOptions                        = {};
+    compileOptions.initializeBuiltinsForInstancedMultiview = true;
+    compileOptions.selectViewInNvGLSLVertexShader          = true;
+    compile(shaderString, &compileOptions);
 
     // The internal ViewID_OVR varying is not exposed through the ShaderVars interface.
     const auto &varyings = mTranslator->getOutputVaryings();
diff --git a/src/tests/compiler_tests/EXT_clip_cull_distance_test.cpp b/src/tests/compiler_tests/EXT_clip_cull_distance_test.cpp
index 3d15ad8..b034e10 100644
--- a/src/tests/compiler_tests/EXT_clip_cull_distance_test.cpp
+++ b/src/tests/compiler_tests/EXT_clip_cull_distance_test.cpp
@@ -255,9 +255,13 @@
 
     testing::AssertionResult TestShaderCompile(const char *pragma)
     {
+        ShCompileOptions compileOptions = {};
+        compileOptions.objectCode       = true;
+        compileOptions.variables        = true;
+
         const char *shaderStrings[] = {testing::get<1>(GetParam()), pragma,
                                        testing::get<2>(GetParam())};
-        bool success = sh::Compile(mCompiler, shaderStrings, 3, SH_VARIABLES | SH_OBJECT_CODE);
+        bool success                = sh::Compile(mCompiler, shaderStrings, 3, compileOptions);
         if (success)
         {
             return ::testing::AssertionSuccess() << "Compilation success";
diff --git a/src/tests/compiler_tests/EXT_shader_framebuffer_fetch_test.cpp b/src/tests/compiler_tests/EXT_shader_framebuffer_fetch_test.cpp
index bf9f038..5225a9d 100644
--- a/src/tests/compiler_tests/EXT_shader_framebuffer_fetch_test.cpp
+++ b/src/tests/compiler_tests/EXT_shader_framebuffer_fetch_test.cpp
@@ -183,11 +183,15 @@
 
     testing::AssertionResult TestShaderCompile(ShShaderOutput shaderOutputType, const char *pragma)
     {
+        ShCompileOptions compileOptions = {};
+        compileOptions.objectCode       = true;
+        compileOptions.variables        = true;
+
         const char *shaderStrings[] = {testing::get<1>(GetParam()), pragma,
                                        testing::get<2>(GetParam())};
 
-        bool success = sh::Compile(mCompilerList[shaderOutputType], shaderStrings, 3,
-                                   SH_VARIABLES | SH_OBJECT_CODE);
+        bool success =
+            sh::Compile(mCompilerList[shaderOutputType], shaderStrings, 3, compileOptions);
         if (success)
         {
             return ::testing::AssertionSuccess()
diff --git a/src/tests/compiler_tests/EmulateGLBaseVertexBaseInstance_test.cpp b/src/tests/compiler_tests/EmulateGLBaseVertexBaseInstance_test.cpp
index 1007410..659387f 100644
--- a/src/tests/compiler_tests/EmulateGLBaseVertexBaseInstance_test.cpp
+++ b/src/tests/compiler_tests/EmulateGLBaseVertexBaseInstance_test.cpp
@@ -19,21 +19,32 @@
 {
   public:
     EmulateGLBaseVertexBaseInstanceTest()
-        : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_VARIABLES, SH_GLSL_COMPATIBILITY_OUTPUT)
+        : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_GLSL_COMPATIBILITY_OUTPUT)
     {
+        ShCompileOptions defaultCompileOptions = {};
+        defaultCompileOptions.variables        = true;
+        setDefaultCompileOptions(defaultCompileOptions);
+
         getResources()->ANGLE_base_vertex_base_instance_shader_builtin = 1;
     }
 
   protected:
     void CheckCompileFailure(const std::string &shaderString,
-                             const char *expectedError       = nullptr,
-                             ShCompileOptions compileOptions = SH_VARIABLES)
+                             const char *expectedError        = nullptr,
+                             ShCompileOptions *compileOptions = nullptr)
     {
+        ShCompileOptions options = {};
+        if (compileOptions != nullptr)
+        {
+            options = *compileOptions;
+        }
+        options.variables = true;
+
         std::string translatedCode;
         std::string infoLog;
-        bool success = compileTestShader(GL_VERTEX_SHADER, SH_GLES3_SPEC,
-                                         SH_GLSL_COMPATIBILITY_OUTPUT, shaderString, getResources(),
-                                         compileOptions, &translatedCode, &infoLog);
+        bool success =
+            compileTestShader(GL_VERTEX_SHADER, SH_GLES3_SPEC, SH_GLSL_COMPATIBILITY_OUTPUT,
+                              shaderString, getResources(), options, &translatedCode, &infoLog);
         EXPECT_FALSE(success);
         if (expectedError)
         {
@@ -65,7 +76,12 @@
         "   gl_Position = vec4(float(gl_BaseVertex), float(gl_BaseInstance), 0.0, 1.0);\n"
         "}\n";
 
-    compile(shaderString, SH_OBJECT_CODE | SH_VARIABLES | SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE);
+    ShCompileOptions compileOptions                = {};
+    compileOptions.objectCode                      = true;
+    compileOptions.variables                       = true;
+    compileOptions.emulateGLBaseVertexBaseInstance = true;
+
+    compile(shaderString, compileOptions);
 }
 
 // Check that compiling with the old extension doesn't work
@@ -78,8 +94,11 @@
         "   gl_Position = vec4(float(gl_BaseVertex), float(gl_BaseInstance), 0.0, 1.0);\n"
         "}\n";
 
-    CheckCompileFailure(shaderString, "extension is not supported",
-                        SH_OBJECT_CODE | SH_VARIABLES | SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE);
+    ShCompileOptions compileOptions                = {};
+    compileOptions.objectCode                      = true;
+    compileOptions.emulateGLBaseVertexBaseInstance = true;
+
+    CheckCompileFailure(shaderString, "extension is not supported", &compileOptions);
 }
 
 // Check that gl_BaseVertex and gl_BaseInstance is properly emulated
@@ -102,7 +121,12 @@
         "   gl_Position = vec4(float(gl_BaseVertex), float(gl_BaseInstance), 0.0, 1.0);\n"
         "}\n";
 
-    compile(shaderString, SH_OBJECT_CODE | SH_VARIABLES | SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE);
+    ShCompileOptions compileOptions                = {};
+    compileOptions.objectCode                      = true;
+    compileOptions.variables                       = true;
+    compileOptions.emulateGLBaseVertexBaseInstance = true;
+
+    compile(shaderString, compileOptions);
 
     EXPECT_TRUE(notFoundInCode("gl_BaseVertex"));
     EXPECT_TRUE(foundInCode("angle_BaseVertex"));
@@ -221,7 +245,12 @@
         "           0.0, 1.0);\n"
         "}\n";
 
-    compile(shaderString, SH_OBJECT_CODE | SH_VARIABLES | SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE);
+    ShCompileOptions compileOptions                = {};
+    compileOptions.objectCode                      = true;
+    compileOptions.variables                       = true;
+    compileOptions.emulateGLBaseVertexBaseInstance = true;
+
+    compile(shaderString, compileOptions);
 
     // " angle_BaseVertex" (note the space) should appear exactly twice:
     //    once in the declaration and once in the body.
diff --git a/src/tests/compiler_tests/EmulateGLDrawID_test.cpp b/src/tests/compiler_tests/EmulateGLDrawID_test.cpp
index 6b6aff6..86412ca 100644
--- a/src/tests/compiler_tests/EmulateGLDrawID_test.cpp
+++ b/src/tests/compiler_tests/EmulateGLDrawID_test.cpp
@@ -18,20 +18,26 @@
 class EmulateGLDrawIDTest : public MatchOutputCodeTest
 {
   public:
-    EmulateGLDrawIDTest()
-        : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_VARIABLES, SH_GLSL_COMPATIBILITY_OUTPUT)
+    EmulateGLDrawIDTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_GLSL_COMPATIBILITY_OUTPUT)
     {
+        ShCompileOptions defaultCompileOptions = {};
+        defaultCompileOptions.variables        = true;
+        setDefaultCompileOptions(defaultCompileOptions);
+
         getResources()->ANGLE_multi_draw = 1;
     }
 
   protected:
     void CheckCompileFailure(const std::string &shaderString, const char *expectedError = nullptr)
     {
+        ShCompileOptions compileOptions = {};
+        compileOptions.variables        = true;
+
         std::string translatedCode;
         std::string infoLog;
         bool success = compileTestShader(GL_VERTEX_SHADER, SH_GLES2_SPEC,
                                          SH_GLSL_COMPATIBILITY_OUTPUT, shaderString, getResources(),
-                                         SH_VARIABLES, &translatedCode, &infoLog);
+                                         compileOptions, &translatedCode, &infoLog);
         EXPECT_FALSE(success);
         if (expectedError)
         {
@@ -61,10 +67,14 @@
         "   gl_Position = vec4(float(gl_DrawID), 0.0, 0.0, 1.0);\n"
         "}\n";
 
-    compile(shaderString, SH_OBJECT_CODE | SH_VARIABLES | SH_EMULATE_GL_DRAW_ID);
-    compile("#version 100\n" + shaderString, SH_OBJECT_CODE | SH_VARIABLES | SH_EMULATE_GL_DRAW_ID);
-    compile("#version 300 es\n" + shaderString,
-            SH_OBJECT_CODE | SH_VARIABLES | SH_EMULATE_GL_DRAW_ID);
+    ShCompileOptions compileOptions = {};
+    compileOptions.objectCode       = true;
+    compileOptions.variables        = true;
+    compileOptions.emulateGLDrawID  = true;
+
+    compile(shaderString, compileOptions);
+    compile("#version 100\n" + shaderString, compileOptions);
+    compile("#version 300 es\n" + shaderString, compileOptions);
 }
 
 // Check that gl_DrawID is properly emulated
@@ -86,7 +96,11 @@
         "   gl_Position = vec4(float(gl_DrawID), 0.0, 0.0, 1.0);\n"
         "}\n";
 
-    compile(shaderString, SH_OBJECT_CODE | SH_VARIABLES | SH_EMULATE_GL_DRAW_ID);
+    ShCompileOptions compileOptions = {};
+    compileOptions.objectCode       = true;
+    compileOptions.variables        = true;
+    compileOptions.emulateGLDrawID  = true;
+    compile(shaderString, compileOptions);
 
     EXPECT_TRUE(notFoundInCode("gl_DrawID"));
     EXPECT_TRUE(foundInCode("angle_DrawID"));
@@ -158,7 +172,11 @@
         "   gl_Position = vec4(float(angle_DrawID + gl_DrawID), 0.0, 0.0, 1.0);\n"
         "}\n";
 
-    compile(shaderString, SH_OBJECT_CODE | SH_VARIABLES | SH_EMULATE_GL_DRAW_ID);
+    ShCompileOptions compileOptions = {};
+    compileOptions.objectCode       = true;
+    compileOptions.variables        = true;
+    compileOptions.emulateGLDrawID  = true;
+    compile(shaderString, compileOptions);
 
     // " angle_DrawID" (note the space) should appear exactly twice:
     //    once in the declaration and once in the body.
diff --git a/src/tests/compiler_tests/EmulateGLFragColorBroadcast_test.cpp b/src/tests/compiler_tests/EmulateGLFragColorBroadcast_test.cpp
index 2e53168..1a93333 100644
--- a/src/tests/compiler_tests/EmulateGLFragColorBroadcast_test.cpp
+++ b/src/tests/compiler_tests/EmulateGLFragColorBroadcast_test.cpp
@@ -23,9 +23,7 @@
 {
   public:
     EmulateGLFragColorBroadcastTest()
-        : MatchOutputCodeTest(GL_FRAGMENT_SHADER,
-                              0,  // compile options
-                              SH_GLSL_COMPATIBILITY_OUTPUT)
+        : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_GLSL_COMPATIBILITY_OUTPUT)
     {
         getResources()->MaxDrawBuffers   = kMaxDrawBuffers;
         getResources()->EXT_draw_buffers = 1;
diff --git a/src/tests/compiler_tests/ExpressionLimit_test.cpp b/src/tests/compiler_tests/ExpressionLimit_test.cpp
index c83cbe2..d399e17 100644
--- a/src/tests/compiler_tests/ExpressionLimit_test.cpp
+++ b/src/tests/compiler_tests/ExpressionLimit_test.cpp
@@ -222,7 +222,7 @@
     // to the issue we are testing.
     bool CheckShaderCompilation(ShHandle compiler,
                                 const char *source,
-                                ShCompileOptions compileOptions,
+                                const ShCompileOptions &compileOptions,
                                 const char *expected_error)
     {
         bool success = sh::Compile(compiler, &source, 1, compileOptions) != 0;
@@ -259,7 +259,8 @@
     ShShaderSpec spec       = SH_WEBGL_SPEC;
     ShShaderOutput output   = SH_ESSL_OUTPUT;
     ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
-    ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
+    ShCompileOptions compileOptions          = {};
+    compileOptions.limitExpressionComplexity = true;
 
     // Test expression under the limit passes.
     EXPECT_TRUE(CheckShaderCompilation(
@@ -270,9 +271,10 @@
         vertexCompiler, GenerateShaderWithLongExpression(kMaxExpressionComplexity + 10).c_str(),
         compileOptions, kExpressionTooComplex));
     // Test expression over the limit without a limit does not fail.
+    compileOptions.limitExpressionComplexity = false;
     EXPECT_TRUE(CheckShaderCompilation(
         vertexCompiler, GenerateShaderWithLongExpression(kMaxExpressionComplexity + 10).c_str(),
-        compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr));
+        compileOptions, nullptr));
     sh::Destruct(vertexCompiler);
 }
 
@@ -281,7 +283,8 @@
     ShShaderSpec spec       = SH_WEBGL_SPEC;
     ShShaderOutput output   = SH_ESSL_OUTPUT;
     ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
-    ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
+    ShCompileOptions compileOptions          = {};
+    compileOptions.limitExpressionComplexity = true;
 
     // Test expression under the limit passes.
     EXPECT_TRUE(CheckShaderCompilation(
@@ -294,10 +297,11 @@
         GenerateShaderWithUnusedLongExpression(kMaxExpressionComplexity + 10).c_str(),
         compileOptions, kExpressionTooComplex));
     // Test expression over the limit without a limit does not fail.
+    compileOptions.limitExpressionComplexity = false;
     EXPECT_TRUE(CheckShaderCompilation(
         vertexCompiler,
         GenerateShaderWithUnusedLongExpression(kMaxExpressionComplexity + 10).c_str(),
-        compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr));
+        compileOptions, nullptr));
     sh::Destruct(vertexCompiler);
 }
 
@@ -306,7 +310,8 @@
     ShShaderSpec spec       = SH_WEBGL_SPEC;
     ShShaderOutput output   = SH_ESSL_OUTPUT;
     ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
-    ShCompileOptions compileOptions = SH_LIMIT_CALL_STACK_DEPTH;
+    ShCompileOptions compileOptions    = {};
+    compileOptions.limitCallStackDepth = true;
 
     // Test call stack under the limit passes.
     EXPECT_TRUE(CheckShaderCompilation(
@@ -317,9 +322,10 @@
         vertexCompiler, GenerateShaderWithDeepFunctionStack(kMaxCallStackDepth + 10).c_str(),
         compileOptions, kCallStackTooDeep));
     // Test call stack over the limit without limit does not fail.
+    compileOptions.limitCallStackDepth = false;
     EXPECT_TRUE(CheckShaderCompilation(
         vertexCompiler, GenerateShaderWithDeepFunctionStack(kMaxCallStackDepth + 10).c_str(),
-        compileOptions & ~SH_LIMIT_CALL_STACK_DEPTH, nullptr));
+        compileOptions, nullptr));
     sh::Destruct(vertexCompiler);
 }
 
@@ -328,7 +334,8 @@
     ShShaderSpec spec       = SH_WEBGL_SPEC;
     ShShaderOutput output   = SH_ESSL_OUTPUT;
     ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
-    ShCompileOptions compileOptions = SH_LIMIT_CALL_STACK_DEPTH;
+    ShCompileOptions compileOptions    = {};
+    compileOptions.limitCallStackDepth = true;
 
     // Test call stack under the limit passes.
     EXPECT_TRUE(CheckShaderCompilation(
@@ -339,9 +346,10 @@
         vertexCompiler, GenerateShaderWithUnusedDeepFunctionStack(kMaxCallStackDepth + 10).c_str(),
         compileOptions, kCallStackTooDeep));
     // Test call stack over the limit without limit does not fail.
+    compileOptions.limitCallStackDepth = false;
     EXPECT_TRUE(CheckShaderCompilation(
         vertexCompiler, GenerateShaderWithUnusedDeepFunctionStack(kMaxCallStackDepth + 10).c_str(),
-        compileOptions & ~SH_LIMIT_CALL_STACK_DEPTH, nullptr));
+        compileOptions, nullptr));
     sh::Destruct(vertexCompiler);
 }
 
@@ -350,7 +358,7 @@
     ShShaderSpec spec       = SH_WEBGL_SPEC;
     ShShaderOutput output   = SH_ESSL_OUTPUT;
     ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
-    ShCompileOptions compileOptions = 0;
+    ShCompileOptions compileOptions = {};
 
     static const char *shaderWithRecursion0 =
         R"(precision mediump float;
@@ -536,15 +544,16 @@
                                        kHasRecursion));
     // Check unused recursions fails if limiting call stack
     // since we check all paths.
-    EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion6,
-                                       compileOptions | SH_LIMIT_CALL_STACK_DEPTH, kHasRecursion));
+    compileOptions.limitCallStackDepth = true;
+    EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion6, compileOptions,
+                                       kHasRecursion));
 
     // Check unused recursions passes.
     EXPECT_TRUE(
         CheckShaderCompilation(vertexCompiler, shaderWithNoRecursion, compileOptions, nullptr));
     // Check unused recursions passes if limiting call stack.
-    EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithNoRecursion,
-                                       compileOptions | SH_LIMIT_CALL_STACK_DEPTH, nullptr));
+    EXPECT_TRUE(
+        CheckShaderCompilation(vertexCompiler, shaderWithNoRecursion, compileOptions, nullptr));
     sh::Destruct(vertexCompiler);
 }
 
@@ -553,7 +562,8 @@
     ShShaderSpec spec     = SH_WEBGL_SPEC;
     ShShaderOutput output = SH_ESSL_OUTPUT;
     ShHandle compiler     = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
-    ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
+    ShCompileOptions compileOptions          = {};
+    compileOptions.limitExpressionComplexity = true;
 
     // Test parameters under the limit succeeds.
     EXPECT_TRUE(CheckShaderCompilation(
@@ -564,9 +574,10 @@
         compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters + 1).c_str(),
         compileOptions, kTooManyParameters));
     // Test parameters over the limit without limit does not fail.
+    compileOptions.limitExpressionComplexity = false;
     EXPECT_TRUE(CheckShaderCompilation(
         compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters + 1).c_str(),
-        compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr));
+        compileOptions, nullptr));
     sh::Destruct(compiler);
 }
 
@@ -575,21 +586,23 @@
     ShShaderSpec spec     = SH_WEBGL2_SPEC;
     ShShaderOutput output = SH_ESSL_OUTPUT;
     ShHandle compiler     = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
-    ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
+    ShCompileOptions compileOptions          = {};
+    compileOptions.limitExpressionComplexity = true;
 
     // Test nesting over the limit fails.
     EXPECT_TRUE(CheckShaderCompilation(
         compiler, GenerateShaderWithNestingInsideSwitch(kMaxExpressionComplexity + 1).c_str(),
         compileOptions, kExpressionTooComplex));
-    // Test nesting over the limit without limit does not fail.
-    EXPECT_TRUE(CheckShaderCompilation(
-        compiler, GenerateShaderWithNestingInsideSwitch(kMaxExpressionComplexity + 1).c_str(),
-        compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr));
     // Test that nesting way over the limit doesn't cause stack overflow but is handled
     // gracefully.
     EXPECT_TRUE(CheckShaderCompilation(compiler,
                                        GenerateShaderWithNestingInsideSwitch(5000).c_str(),
                                        compileOptions, kTooComplexSwitch));
+    // Test nesting over the limit without limit does not fail.
+    compileOptions.limitExpressionComplexity = false;
+    EXPECT_TRUE(CheckShaderCompilation(
+        compiler, GenerateShaderWithNestingInsideSwitch(kMaxExpressionComplexity + 1).c_str(),
+        compileOptions, nullptr));
     sh::Destruct(compiler);
 }
 
@@ -598,22 +611,24 @@
     ShShaderSpec spec     = SH_WEBGL_SPEC;
     ShShaderOutput output = SH_ESSL_OUTPUT;
     ShHandle compiler     = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
-    ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
+    ShCompileOptions compileOptions          = {};
+    compileOptions.limitExpressionComplexity = true;
 
     // Test nesting over the limit fails.
     EXPECT_TRUE(CheckShaderCompilation(
         compiler,
         GenerateShaderWithNestingInsideGlobalInitializer(kMaxExpressionComplexity + 1).c_str(),
         compileOptions, kExpressionTooComplex));
-    // Test nesting over the limit without limit does not fail.
-    EXPECT_TRUE(CheckShaderCompilation(
-        compiler,
-        GenerateShaderWithNestingInsideGlobalInitializer(kMaxExpressionComplexity + 1).c_str(),
-        compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr));
     // Test that nesting way over the limit doesn't cause stack overflow but is handled
     // gracefully.
     EXPECT_TRUE(CheckShaderCompilation(
         compiler, GenerateShaderWithNestingInsideGlobalInitializer(5000).c_str(), compileOptions,
         kGlobalVariableInit));
+    // Test nesting over the limit without limit does not fail.
+    compileOptions.limitExpressionComplexity = false;
+    EXPECT_TRUE(CheckShaderCompilation(
+        compiler,
+        GenerateShaderWithNestingInsideGlobalInitializer(kMaxExpressionComplexity + 1).c_str(),
+        compileOptions, nullptr));
     sh::Destruct(compiler);
 }
diff --git a/src/tests/compiler_tests/FragDepth_test.cpp b/src/tests/compiler_tests/FragDepth_test.cpp
index 9d725c8..674661c 100644
--- a/src/tests/compiler_tests/FragDepth_test.cpp
+++ b/src/tests/compiler_tests/FragDepth_test.cpp
@@ -51,7 +51,7 @@
                                                const char *shader)
     {
         const char *shaderStrings[] = {version, pragma, shader};
-        bool success                = sh::Compile(mCompiler, shaderStrings, 3, 0);
+        bool success                = sh::Compile(mCompiler, shaderStrings, 3, {});
         if (success)
         {
             return ::testing::AssertionSuccess() << "Compilation success";
diff --git a/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp b/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp
index 92874ca..f1c611b 100644
--- a/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp
+++ b/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp
@@ -18,7 +18,7 @@
 {
   public:
     GLSLCompatibilityOutputTest()
-        : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_VARIABLES, SH_GLSL_COMPATIBILITY_OUTPUT)
+        : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_GLSL_COMPATIBILITY_OUTPUT)
     {}
 };
 
diff --git a/src/tests/compiler_tests/GeometryShader_test.cpp b/src/tests/compiler_tests/GeometryShader_test.cpp
index f69120e..62a5195 100644
--- a/src/tests/compiler_tests/GeometryShader_test.cpp
+++ b/src/tests/compiler_tests/GeometryShader_test.cpp
@@ -114,9 +114,12 @@
 class GeometryShaderOutputCodeTest : public MatchOutputCodeTest
 {
   public:
-    GeometryShaderOutputCodeTest()
-        : MatchOutputCodeTest(GL_GEOMETRY_SHADER_EXT, SH_OBJECT_CODE, SH_ESSL_OUTPUT)
+    GeometryShaderOutputCodeTest() : MatchOutputCodeTest(GL_GEOMETRY_SHADER_EXT, SH_ESSL_OUTPUT)
     {
+        ShCompileOptions defaultCompileOptions = {};
+        defaultCompileOptions.objectCode       = true;
+        setDefaultCompileOptions(defaultCompileOptions);
+
         getResources()->EXT_geometry_shader = 1;
     }
 };
diff --git a/src/tests/compiler_tests/HLSLOutput_test.cpp b/src/tests/compiler_tests/HLSLOutput_test.cpp
index 1d75dbe..47a6c97 100644
--- a/src/tests/compiler_tests/HLSLOutput_test.cpp
+++ b/src/tests/compiler_tests/HLSLOutput_test.cpp
@@ -18,19 +18,19 @@
 class HLSLOutputTest : public MatchOutputCodeTest
 {
   public:
-    HLSLOutputTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_HLSL_4_1_OUTPUT) {}
+    HLSLOutputTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_HLSL_4_1_OUTPUT) {}
 };
 
 class HLSL30VertexOutputTest : public MatchOutputCodeTest
 {
   public:
-    HLSL30VertexOutputTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, 0, SH_HLSL_3_0_OUTPUT) {}
+    HLSL30VertexOutputTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_HLSL_3_0_OUTPUT) {}
 };
 
 class HLSL41VertexOutputTest : public MatchOutputCodeTest
 {
   public:
-    HLSL41VertexOutputTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, 0, SH_HLSL_4_1_OUTPUT) {}
+    HLSL41VertexOutputTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_HLSL_4_1_OUTPUT) {}
 };
 
 // Test that having dynamic indexing of a vector inside the right hand side of logical or doesn't
diff --git a/src/tests/compiler_tests/InitOutputVariables_test.cpp b/src/tests/compiler_tests/InitOutputVariables_test.cpp
index 9ec47ed..91aa4b8 100644
--- a/src/tests/compiler_tests/InitOutputVariables_test.cpp
+++ b/src/tests/compiler_tests/InitOutputVariables_test.cpp
@@ -191,11 +191,11 @@
   public:
     void SetUp() override
     {
-        mExtraCompileOptions |= SH_VARIABLES;
-        mExtraCompileOptions |= SH_INIT_OUTPUT_VARIABLES;
+        mCompileOptions.variables           = true;
+        mCompileOptions.initOutputVariables = true;
         if (getShaderType() == GL_VERTEX_SHADER)
         {
-            mExtraCompileOptions |= SH_INIT_GL_POSITION;
+            mCompileOptions.initGLPosition = true;
         }
         ShaderCompileTreeTest::SetUp();
     }
@@ -226,8 +226,8 @@
   public:
     InitOutputVariablesWebGL1FragmentShaderTest()
     {
-        mExtraCompileOptions |= SH_VARIABLES;
-        mExtraCompileOptions |= SH_INIT_OUTPUT_VARIABLES;
+        mCompileOptions.variables           = true;
+        mCompileOptions.initOutputVariables = true;
     }
 
   protected:
@@ -245,9 +245,9 @@
   public:
     InitOutputVariablesVertexShaderClipDistanceTest()
     {
-        mExtraCompileOptions |= SH_VARIABLES;
-        mExtraCompileOptions |= SH_INIT_OUTPUT_VARIABLES;
-        mExtraCompileOptions |= SH_VALIDATE_AST;
+        mCompileOptions.variables           = true;
+        mCompileOptions.initOutputVariables = true;
+        mCompileOptions.validateAST         = true;
     }
 
   protected:
diff --git a/src/tests/compiler_tests/KHR_blend_equation_advanced_test.cpp b/src/tests/compiler_tests/KHR_blend_equation_advanced_test.cpp
index b3c302d..5c01696 100644
--- a/src/tests/compiler_tests/KHR_blend_equation_advanced_test.cpp
+++ b/src/tests/compiler_tests/KHR_blend_equation_advanced_test.cpp
@@ -165,10 +165,12 @@
         const char *shaderStrings[] = {testing::get<1>(GetParam()), pragma,
                                        testing::get<2>(GetParam())};
 
-        ShCompileOptions compileFlags = SH_VARIABLES | SH_OBJECT_CODE;
+        ShCompileOptions compileFlags = {};
+        compileFlags.variables        = true;
+        compileFlags.objectCode       = true;
         if (emulate == Emulation::Enabled)
         {
-            compileFlags |= SH_ADD_ADVANCED_BLEND_EQUATIONS_EMULATION;
+            compileFlags.addAdvancedBlendEquationsEmulation = true;
         }
 
         bool success = sh::Compile(mCompilerList[shaderOutputType], shaderStrings, 3, compileFlags);
diff --git a/src/tests/compiler_tests/MSLOutput_test.cpp b/src/tests/compiler_tests/MSLOutput_test.cpp
index 67d6272..9a5ee81 100644
--- a/src/tests/compiler_tests/MSLOutput_test.cpp
+++ b/src/tests/compiler_tests/MSLOutput_test.cpp
@@ -18,13 +18,23 @@
 class MSLVertexOutputTest : public MatchOutputCodeTest
 {
   public:
-    MSLVertexOutputTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, 0, SH_MSL_METAL_OUTPUT) {}
+    MSLVertexOutputTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_MSL_METAL_OUTPUT)
+    {
+        ShCompileOptions defaultCompileOptions = {};
+        defaultCompileOptions.variables        = true;
+        setDefaultCompileOptions(defaultCompileOptions);
+    }
 };
 
 class MSLOutputTest : public MatchOutputCodeTest
 {
   public:
-    MSLOutputTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_MSL_METAL_OUTPUT) {}
+    MSLOutputTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_MSL_METAL_OUTPUT)
+    {
+        ShCompileOptions defaultCompileOptions = {};
+        defaultCompileOptions.variables        = true;
+        setDefaultCompileOptions(defaultCompileOptions);
+    }
 };
 
 // Test that having dynamic indexing of a vector inside the right hand side of logical or doesn't
@@ -40,7 +50,7 @@
         "   bvec4 v = bvec4(true, true, true, false);\n"
         "   my_FragColor = vec4(v[u1 + 1] || v[u1]);\n"
         "}\n";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 // Test that having an array constructor as a statement doesn't trigger an assert in MSL output.
@@ -55,7 +65,7 @@
             outColor = vec4(0.0, 0.0, 0.0, 1.0);
             float[1](outColor[1]++);
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 // Test an array of arrays constructor as a statement.
@@ -70,7 +80,7 @@
             outColor = vec4(0.0, 0.0, 0.0, 1.0);
             float[2][2](float[2](outColor[1]++, 0.0), float[2](1.0, 2.0));
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 // Test dynamic indexing of a vector. This makes sure that helper functions added for dynamic
@@ -88,7 +98,7 @@
             foo[i] = foo[i + 1];
             outColor = foo;
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 // Test returning an array from a user-defined function. This makes sure that function symbols are
@@ -111,7 +121,7 @@
             float[2] arr = getArray(u);
             outColor = vec4(arr[0], arr[1], 0.0, 1.0);
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 // Test that writing parameters without a name doesn't assert.
@@ -130,7 +140,7 @@
         {
             gl_FragColor = s(v);
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, Macro)
@@ -147,7 +157,7 @@
         {
             outColor = FOO(1.0, 2.0, 3.0, 4.0);
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, UniformSimple)
@@ -163,7 +173,7 @@
         {
             outColor = vec4(x, x, x, x);
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, FragmentOutSimple)
@@ -178,7 +188,7 @@
         {
             outColor = vec4(1.0, 2.0, 3.0, 4.0);
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, FragmentOutIndirect1)
@@ -203,7 +213,7 @@
         {
             bar();
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, FragmentOutIndirect2)
@@ -230,7 +240,7 @@
         {
             bar();
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, FragmentOutIndirect3)
@@ -266,7 +276,7 @@
         {
             identity(bar(baz()));
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, VertexInOut)
@@ -280,7 +290,7 @@
         {
             out0 = in0;
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, SymbolSharing)
@@ -311,7 +321,7 @@
             foo.y = 2.0;
             doFoo(foo, 3.0);
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, StructDecl)
@@ -332,7 +342,7 @@
             out0 = foo.value;
         }
         )";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, Structs)
@@ -381,7 +391,7 @@
         }
 
         )";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, KeywordConflict)
@@ -416,7 +426,7 @@
         {
             metal(stage_in(stage_in(kernel * device.kernel)), foo.frag.kernel);
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLVertexOutputTest, Vertex)
@@ -428,7 +438,7 @@
         {
             gl_Position = vec4(1.0,1.0,1.0,1.0);
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLVertexOutputTest, LastReturn)
@@ -445,7 +455,7 @@
             v_color = vec4(a_coords.xyz, 1.0);
             return;
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, LastReturn)
@@ -460,7 +470,7 @@
             o_color = vec4(v_coords.xyz, 1.0);
             return;
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, FragColor)
@@ -470,7 +480,7 @@
         {
             gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, MatrixIn)
@@ -487,7 +497,7 @@
             out0 = mat[0][0];
         }
         )";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, WhileTrue)
@@ -507,7 +517,7 @@
                 break;
             }
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, ForTrue)
@@ -527,7 +537,7 @@
                 break;
             }
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, ForEmpty)
@@ -547,7 +557,7 @@
                 break;
             }
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, ForComplex)
@@ -568,7 +578,7 @@
                 my_FragColor.x += float(i);
             }
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, ForSymbol)
@@ -589,7 +599,7 @@
                 cond = false;
             }
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, DoWhileSymbol)
@@ -609,7 +619,7 @@
                 my_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
             } while (cond);
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
 }
 
 TEST_F(MSLOutputTest, AnonymousStruct)
@@ -622,7 +632,7 @@
             anonStruct.v = vec4(0.0,1.0,0.0,1.0);
             gl_FragColor = anonStruct.v;
         })";
-    compile(shaderString, SH_VARIABLES);
+    compile(shaderString);
     // TODO(anglebug.com/6395): This success condition is expected to fail now.
     // When WebKit build is able to run the tests, this should be changed to something else.
     //    ASSERT_TRUE(foundInCode(SH_MSL_METAL_OUTPUT, "__unnamed"));
diff --git a/src/tests/compiler_tests/NV_draw_buffers_test.cpp b/src/tests/compiler_tests/NV_draw_buffers_test.cpp
index efd1e09..0c1126f 100644
--- a/src/tests/compiler_tests/NV_draw_buffers_test.cpp
+++ b/src/tests/compiler_tests/NV_draw_buffers_test.cpp
@@ -17,7 +17,7 @@
 class NVDrawBuffersTest : public MatchOutputCodeTest
 {
   public:
-    NVDrawBuffersTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_ESSL_OUTPUT)
+    NVDrawBuffersTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT)
     {
         ShBuiltInResources *resources = getResources();
         resources->MaxDrawBuffers     = 8;
diff --git a/src/tests/compiler_tests/OES_sample_variables_test.cpp b/src/tests/compiler_tests/OES_sample_variables_test.cpp
index f555cdb..48d33a8 100644
--- a/src/tests/compiler_tests/OES_sample_variables_test.cpp
+++ b/src/tests/compiler_tests/OES_sample_variables_test.cpp
@@ -76,7 +76,12 @@
     {
         const char *shaderStrings[] = {testing::get<1>(GetParam()), pragma,
                                        testing::get<2>(GetParam())};
-        bool success = sh::Compile(mCompiler, shaderStrings, 3, SH_VARIABLES | SH_OBJECT_CODE);
+
+        ShCompileOptions compileOptions = {};
+        compileOptions.objectCode       = true;
+        compileOptions.variables        = true;
+
+        bool success = sh::Compile(mCompiler, shaderStrings, 3, compileOptions);
         if (success)
         {
             return ::testing::AssertionSuccess() << "Compilation success";
diff --git a/src/tests/compiler_tests/OVR_multiview2_test.cpp b/src/tests/compiler_tests/OVR_multiview2_test.cpp
index c30e392..04c9100 100644
--- a/src/tests/compiler_tests/OVR_multiview2_test.cpp
+++ b/src/tests/compiler_tests/OVR_multiview2_test.cpp
@@ -123,13 +123,16 @@
 {
   public:
     OVRMultiview2OutputCodeTest(sh::GLenum shaderType)
-        : MatchOutputCodeTest(shaderType, 0, SH_ESSL_OUTPUT)
+        : MatchOutputCodeTest(shaderType, SH_ESSL_OUTPUT), mMultiviewCompileOptions{}
     {
         addOutputType(SH_GLSL_COMPATIBILITY_OUTPUT);
 
         getResources()->OVR_multiview  = 1;
         getResources()->OVR_multiview2 = 1;
         getResources()->MaxViewsOVR    = 4;
+
+        mMultiviewCompileOptions.initializeBuiltinsForInstancedMultiview = true;
+        mMultiviewCompileOptions.selectViewInNvGLSLVertexShader          = true;
     }
 
     void requestHLSLOutput()
@@ -152,6 +155,9 @@
         return true;
 #endif
     }
+
+  protected:
+    ShCompileOptions mMultiviewCompileOptions;
 };
 
 class OVRMultiview2VertexShaderOutputCodeTest : public OVRMultiview2OutputCodeTest
@@ -469,7 +475,7 @@
         "   myInstanceF = float(gl_InstanceID) + .5;\n"
         "   myInstanceF2 = float(gl_InstanceID) + .1;\n"
         "}\n";
-    mExtraCompileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
+    mCompileOptions.initializeBuiltinsForInstancedMultiview = true;
     compileAssumeSuccess(shaderString);
 
     SymbolOccurrenceCounterByName glInstanceIDByName(ImmutableString("gl_InstanceID"));
@@ -501,7 +507,7 @@
         "   gl_Position.yzw = vec3(0., 0., 1.);\n"
         "   a = gl_ViewID_OVR == 0u ? (gl_ViewID_OVR+2u) : gl_ViewID_OVR;\n"
         "}\n";
-    mExtraCompileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
+    mCompileOptions.initializeBuiltinsForInstancedMultiview = true;
     compileAssumeSuccess(shaderString);
 
     SymbolOccurrenceCounterByName glViewIDOVRByName(ImmutableString("gl_ViewID_OVR"));
@@ -534,7 +540,9 @@
         "   myInstance = gl_InstanceID;\n"
         "}\n";
     requestHLSLOutput();
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW);
+    ShCompileOptions compileOptions                        = {};
+    compileOptions.initializeBuiltinsForInstancedMultiview = true;
+    compile(shaderString, compileOptions);
 
     EXPECT_TRUE(foundInAllGLSLCode("ViewID_OVR = (uint(gl_InstanceID) % 3u)"));
     EXPECT_TRUE(foundInAllGLSLCode("InstanceID = int((uint(gl_InstanceID) / 3u))"));
@@ -558,7 +566,9 @@
         "{\n"
         "}\n";
     // The directive must not be present if any of the multiview emulation options are set.
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW);
+    ShCompileOptions compileOptions                        = {};
+    compileOptions.initializeBuiltinsForInstancedMultiview = true;
+    compile(shaderString, compileOptions);
     EXPECT_FALSE(foundInESSLCode("GL_OVR_multiview2"));
     EXPECT_FALSE(foundInGLSLCode("GL_OVR_multiview2"));
 
@@ -577,7 +587,7 @@
         "void main()\n"
         "{\n"
         "}\n";
-    mExtraCompileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
+    mCompileOptions.initializeBuiltinsForInstancedMultiview = true;
     compileAssumeSuccess(shaderString);
     VariableOccursNTimes(mASTRoot, ImmutableString("ViewID_OVR"), EvqFlatIn, 1u);
 }
@@ -609,7 +619,7 @@
         "void main()\n"
         "{\n"
         "}\n";
-    mExtraCompileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
+    mCompileOptions.initializeBuiltinsForInstancedMultiview = true;
     compileAssumeSuccess(shaderString);
     VariableOccursNTimes(mASTRoot, ImmutableString("ViewID_OVR"), EvqFlatOut, 2u);
 }
@@ -634,7 +644,7 @@
 }
 
 // The test checks that the GL_NV_viewport_array2 extension is emitted in a vertex shader if the
-// SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set.
+// selectViewInNvGLSLVertexShader option is set.
 TEST_F(OVRMultiview2VertexShaderOutputCodeTest, ViewportArray2IsEmitted)
 {
     const std::string &shaderString =
@@ -644,14 +654,13 @@
         "void main()\n"
         "{\n"
         "}\n";
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    compile(shaderString, mMultiviewCompileOptions);
     EXPECT_TRUE(foundInAllGLSLCode("#extension GL_NV_viewport_array2 : require"));
 }
 
 // The test checks that the GL_NV_viewport_array2 extension is not emitted in a vertex shader if the
 // OVR_multiview2 extension is not requested in the shader source even if the
-// SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set.
+// selectViewInNvGLSLVertexShader option is set.
 TEST_F(OVRMultiview2VertexShaderOutputCodeTest, ViewportArray2IsNotEmitted)
 {
     const std::string &shaderString =
@@ -659,14 +668,13 @@
         "void main()\n"
         "{\n"
         "}\n";
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    compile(shaderString, mMultiviewCompileOptions);
     EXPECT_FALSE(foundInGLSLCode("#extension GL_NV_viewport_array2"));
     EXPECT_FALSE(foundInESSLCode("#extension GL_NV_viewport_array2"));
 }
 
 // The test checks that the GL_NV_viewport_array2 extension is not emitted in a fragment shader if
-// the SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set.
+// the selectViewInNvGLSLVertexShader option is set.
 TEST_F(OVRMultiview2FragmentShaderOutputCodeTest, ViewportArray2IsNotEmitted)
 {
     const std::string &shaderString =
@@ -675,8 +683,7 @@
         "void main()\n"
         "{\n"
         "}\n";
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    compile(shaderString, mMultiviewCompileOptions);
     EXPECT_FALSE(foundInGLSLCode("#extension GL_NV_viewport_array2"));
     EXPECT_FALSE(foundInESSLCode("#extension GL_NV_viewport_array2"));
 }
@@ -706,7 +713,7 @@
 }
 
 // The test checks that the GL_NV_viewport_array2 extension is not emitted in a compute shader if
-// the SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set.
+// the selectViewInNvGLSLVertexShader option is set.
 TEST_F(OVRMultiview2ComputeShaderOutputCodeTest, ViewportArray2IsNotEmitted)
 {
     const std::string &shaderString =
@@ -715,8 +722,7 @@
         void main()
         {
         })";
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    compile(shaderString, mMultiviewCompileOptions);
     EXPECT_FALSE(foundInGLSLCode("#extension GL_NV_viewport_array2"));
     EXPECT_FALSE(foundInESSLCode("#extension GL_NV_viewport_array2"));
 }
@@ -732,8 +738,7 @@
         "void main()\n"
         "{\n"
         "}\n";
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    compile(shaderString, mMultiviewCompileOptions);
 
     std::vector<const char *> expectedStrings = {"ViewID_OVR = (uint(gl_InstanceID) % 3u)",
                                                  "gl_ViewportIndex = int(ViewID_OVR)"};
@@ -752,8 +757,7 @@
         "void main()\n"
         "{\n"
         "}\n";
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    compile(shaderString, mMultiviewCompileOptions);
 
     std::vector<const char *> expectedStrings = {
         "ViewID_OVR = (uint(gl_InstanceID) % 3u)",
diff --git a/src/tests/compiler_tests/OVR_multiview_test.cpp b/src/tests/compiler_tests/OVR_multiview_test.cpp
index de2dfa0..0f2f2ba 100644
--- a/src/tests/compiler_tests/OVR_multiview_test.cpp
+++ b/src/tests/compiler_tests/OVR_multiview_test.cpp
@@ -121,12 +121,15 @@
 {
   public:
     OVRMultiviewOutputCodeTest(sh::GLenum shaderType)
-        : MatchOutputCodeTest(shaderType, 0, SH_ESSL_OUTPUT)
+        : MatchOutputCodeTest(shaderType, SH_ESSL_OUTPUT), mMultiviewCompileOptions{}
     {
         addOutputType(SH_GLSL_COMPATIBILITY_OUTPUT);
 
         getResources()->OVR_multiview = 1;
         getResources()->MaxViewsOVR   = 4;
+
+        mMultiviewCompileOptions.initializeBuiltinsForInstancedMultiview = true;
+        mMultiviewCompileOptions.selectViewInNvGLSLVertexShader          = true;
     }
 
     void requestHLSLOutput()
@@ -149,6 +152,9 @@
         return true;
 #endif
     }
+
+  protected:
+    ShCompileOptions mMultiviewCompileOptions;
 };
 
 class OVRMultiviewVertexShaderOutputCodeTest : public OVRMultiviewOutputCodeTest
@@ -485,7 +491,7 @@
         "   myInstanceF = float(gl_InstanceID) + .5;\n"
         "   myInstanceF2 = float(gl_InstanceID) + .1;\n"
         "}\n";
-    mExtraCompileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
+    mCompileOptions.initializeBuiltinsForInstancedMultiview = true;
     compileAssumeSuccess(shaderString);
 
     SymbolOccurrenceCounterByName glInstanceIDByName(ImmutableString("gl_InstanceID"));
@@ -517,7 +523,7 @@
         "   gl_Position.yzw = vec3(0., 0., 1.);\n"
         "   a = gl_ViewID_OVR == 0u ? (gl_ViewID_OVR+2u) : gl_ViewID_OVR;\n"
         "}\n";
-    mExtraCompileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
+    mCompileOptions.initializeBuiltinsForInstancedMultiview = true;
     compileAssumeSuccess(shaderString);
 
     SymbolOccurrenceCounterByName glViewIDOVRByName(ImmutableString("gl_ViewID_OVR"));
@@ -550,7 +556,11 @@
         "   myInstance = gl_InstanceID;\n"
         "}\n";
     requestHLSLOutput();
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW);
+
+    ShCompileOptions compileOptions                        = {};
+    compileOptions.initializeBuiltinsForInstancedMultiview = true;
+
+    compile(shaderString, compileOptions);
 
     EXPECT_TRUE(foundInAllGLSLCode("ViewID_OVR = (uint(gl_InstanceID) % 3u)"));
     EXPECT_TRUE(foundInAllGLSLCode("InstanceID = int((uint(gl_InstanceID) / 3u))"));
@@ -574,7 +584,9 @@
         "{\n"
         "}\n";
     // The directive must not be present if any of the multiview emulation options are set.
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW);
+    ShCompileOptions compileOptions                        = {};
+    compileOptions.initializeBuiltinsForInstancedMultiview = true;
+    compile(shaderString, compileOptions);
     EXPECT_FALSE(foundInESSLCode("GL_OVR_multiview"));
     EXPECT_FALSE(foundInGLSLCode("GL_OVR_multiview"));
 
@@ -593,7 +605,7 @@
         "void main()\n"
         "{\n"
         "}\n";
-    mExtraCompileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
+    mCompileOptions.initializeBuiltinsForInstancedMultiview = true;
     compileAssumeSuccess(shaderString);
     VariableOccursNTimes(mASTRoot, ImmutableString("ViewID_OVR"), EvqFlatIn, 1u);
 }
@@ -606,13 +618,13 @@
         "void main()\n"
         "{\n"
         "}\n";
-    mExtraCompileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
+    mCompileOptions.initializeBuiltinsForInstancedMultiview = true;
     compileAssumeSuccess(shaderString);
     VariableOccursNTimes(mASTRoot, ImmutableString("ViewID_OVR"), EvqFlatOut, 2u);
 }
 
 // The test checks that the GL_NV_viewport_array2 extension is emitted in a vertex shader if the
-// SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set.
+// selectViewInNvGLSLVertexShader option is set.
 TEST_F(OVRMultiviewVertexShaderOutputCodeTest, ViewportArray2IsEmitted)
 {
     const std::string &shaderString =
@@ -622,14 +634,13 @@
         "void main()\n"
         "{\n"
         "}\n";
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    compile(shaderString, mMultiviewCompileOptions);
     EXPECT_TRUE(foundInAllGLSLCode("#extension GL_NV_viewport_array2 : require"));
 }
 
 // The test checks that the GL_NV_viewport_array2 extension is not emitted in a vertex shader if the
 // OVR_multiview2 extension is not requested in the shader source even if the
-// SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set.
+// selectViewInNvGLSLVertexShader option is set.
 TEST_F(OVRMultiviewVertexShaderOutputCodeTest, ViewportArray2IsNotEmitted)
 {
     const std::string &shaderString =
@@ -637,14 +648,13 @@
         "void main()\n"
         "{\n"
         "}\n";
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    compile(shaderString, mMultiviewCompileOptions);
     EXPECT_FALSE(foundInGLSLCode("#extension GL_NV_viewport_array2"));
     EXPECT_FALSE(foundInESSLCode("#extension GL_NV_viewport_array2"));
 }
 
 // The test checks that the GL_NV_viewport_array2 extension is not emitted in a fragment shader if
-// the SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set.
+// the selectViewInNvGLSLVertexShader option is set.
 TEST_F(OVRMultiviewFragmentShaderOutputCodeTest, ViewportArray2IsNotEmitted)
 {
     const std::string &shaderString =
@@ -653,8 +663,7 @@
         "void main()\n"
         "{\n"
         "}\n";
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    compile(shaderString, mMultiviewCompileOptions);
     EXPECT_FALSE(foundInGLSLCode("#extension GL_NV_viewport_array2"));
     EXPECT_FALSE(foundInESSLCode("#extension GL_NV_viewport_array2"));
 }
@@ -687,7 +696,7 @@
 }
 
 // The test checks that the GL_NV_viewport_array2 extension is not emitted in a compute shader if
-// the SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set.
+// the selectViewInNvGLSLVertexShader option is set.
 TEST_F(OVRMultiviewComputeShaderOutputCodeTest, ViewportArray2IsNotEmitted)
 {
     const std::string &shaderString =
@@ -696,8 +705,7 @@
         void main()
         {
         })";
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    compile(shaderString, mMultiviewCompileOptions);
     EXPECT_FALSE(foundInGLSLCode("#extension GL_NV_viewport_array2"));
     EXPECT_FALSE(foundInESSLCode("#extension GL_NV_viewport_array2"));
 }
@@ -713,8 +721,7 @@
         "void main()\n"
         "{\n"
         "}\n";
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    compile(shaderString, mMultiviewCompileOptions);
 
     std::vector<const char *> expectedStrings = {"ViewID_OVR = (uint(gl_InstanceID) % 3u)",
                                                  "gl_ViewportIndex = int(ViewID_OVR)"};
@@ -733,8 +740,7 @@
         "void main()\n"
         "{\n"
         "}\n";
-    compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
-                              SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER);
+    compile(shaderString, mMultiviewCompileOptions);
 
     std::vector<const char *> expectedStrings = {
         "ViewID_OVR = (uint(gl_InstanceID) % 3u)",
@@ -782,4 +788,4 @@
     EXPECT_FALSE(foundInCodeInOrder(SH_GLSL_COMPATIBILITY_OUTPUT, notExpectedStrings2));
 }
 
-}  // namespace
\ No newline at end of file
+}  // namespace
diff --git a/src/tests/compiler_tests/Pack_Unpack_test.cpp b/src/tests/compiler_tests/Pack_Unpack_test.cpp
index 0de5310..629009b 100644
--- a/src/tests/compiler_tests/Pack_Unpack_test.cpp
+++ b/src/tests/compiler_tests/Pack_Unpack_test.cpp
@@ -20,7 +20,7 @@
 class PackUnpackTest : public MatchOutputCodeTest
 {
   public:
-    PackUnpackTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_GLSL_400_CORE_OUTPUT) {}
+    PackUnpackTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_GLSL_400_CORE_OUTPUT) {}
 };
 
 // Check if PackSnorm2x16 Emulation for GLSL < 4.2 compile correctly.
diff --git a/src/tests/compiler_tests/Precise_test.cpp b/src/tests/compiler_tests/Precise_test.cpp
index 784413e..78f83f7 100644
--- a/src/tests/compiler_tests/Precise_test.cpp
+++ b/src/tests/compiler_tests/Precise_test.cpp
@@ -80,7 +80,9 @@
     {
         const char *shaderStrings[] = {shaderSource};
 
-        const ShCompileOptions options = SH_VARIABLES | SH_OBJECT_CODE;
+        ShCompileOptions options = {};
+        options.variables        = true;
+        options.objectCode       = true;
 
         bool success = sh::Compile(mCompilerList[shaderOutputType], shaderStrings, 1, options);
         if (success)
diff --git a/src/tests/compiler_tests/PruneEmptyCases_test.cpp b/src/tests/compiler_tests/PruneEmptyCases_test.cpp
index b63a017..4b1871c 100644
--- a/src/tests/compiler_tests/PruneEmptyCases_test.cpp
+++ b/src/tests/compiler_tests/PruneEmptyCases_test.cpp
@@ -21,8 +21,7 @@
 class PruneEmptyCasesTest : public MatchOutputCodeTest
 {
   public:
-    PruneEmptyCasesTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_GLSL_COMPATIBILITY_OUTPUT)
-    {}
+    PruneEmptyCasesTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_GLSL_COMPATIBILITY_OUTPUT) {}
 };
 
 // Test that a switch statement that only contains no-ops is pruned entirely.
diff --git a/src/tests/compiler_tests/PruneEmptyDeclarations_test.cpp b/src/tests/compiler_tests/PruneEmptyDeclarations_test.cpp
index b421c7c..c7bac70 100644
--- a/src/tests/compiler_tests/PruneEmptyDeclarations_test.cpp
+++ b/src/tests/compiler_tests/PruneEmptyDeclarations_test.cpp
@@ -21,7 +21,7 @@
 {
   public:
     PruneEmptyDeclarationsTest()
-        : MatchOutputCodeTest(GL_VERTEX_SHADER, 0, SH_GLSL_COMPATIBILITY_OUTPUT)
+        : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_GLSL_COMPATIBILITY_OUTPUT)
     {}
 };
 
diff --git a/src/tests/compiler_tests/PrunePureLiteralStatements_test.cpp b/src/tests/compiler_tests/PrunePureLiteralStatements_test.cpp
index f176741..910ccc5 100644
--- a/src/tests/compiler_tests/PrunePureLiteralStatements_test.cpp
+++ b/src/tests/compiler_tests/PrunePureLiteralStatements_test.cpp
@@ -20,7 +20,7 @@
 {
   public:
     // The PrunePureLiteralStatements pass is used when outputting ESSL
-    PrunePureLiteralStatementsTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_ESSL_OUTPUT) {}
+    PrunePureLiteralStatementsTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT) {}
 };
 
 // Most basic test for the pruning
diff --git a/src/tests/compiler_tests/PruneUnusedFunctions_test.cpp b/src/tests/compiler_tests/PruneUnusedFunctions_test.cpp
index 3d40ac5..a5a00d9 100644
--- a/src/tests/compiler_tests/PruneUnusedFunctions_test.cpp
+++ b/src/tests/compiler_tests/PruneUnusedFunctions_test.cpp
@@ -20,13 +20,15 @@
 class PruneUnusedFunctionsTest : public MatchOutputCodeTest
 {
   public:
-    PruneUnusedFunctionsTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_ESSL_OUTPUT) {}
+    PruneUnusedFunctionsTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT) {}
 
   protected:
     void compile(const std::string &shaderString)
     {
-        int compilationFlags = SH_VARIABLES;
-        MatchOutputCodeTest::compile(shaderString, compilationFlags);
+        ShCompileOptions compileOptions = {};
+        compileOptions.variables        = true;
+
+        MatchOutputCodeTest::compile(shaderString, compileOptions);
     }
 };
 
diff --git a/src/tests/compiler_tests/RecordConstantPrecision_test.cpp b/src/tests/compiler_tests/RecordConstantPrecision_test.cpp
index 4e7dcce..07923f9 100644
--- a/src/tests/compiler_tests/RecordConstantPrecision_test.cpp
+++ b/src/tests/compiler_tests/RecordConstantPrecision_test.cpp
@@ -17,7 +17,7 @@
 class RecordConstantPrecisionTest : public MatchOutputCodeTest
 {
   public:
-    RecordConstantPrecisionTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_ESSL_OUTPUT) {}
+    RecordConstantPrecisionTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT) {}
 };
 
 // The constant's precision must be specified if its precision is higher than the other operands,
diff --git a/src/tests/compiler_tests/RegenerateStructNames_test.cpp b/src/tests/compiler_tests/RegenerateStructNames_test.cpp
index a55c9fb..8f873e7 100644
--- a/src/tests/compiler_tests/RegenerateStructNames_test.cpp
+++ b/src/tests/compiler_tests/RegenerateStructNames_test.cpp
@@ -17,9 +17,12 @@
 class RegenerateStructNamesTest : public MatchOutputCodeTest
 {
   public:
-    RegenerateStructNamesTest()
-        : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_REGENERATE_STRUCT_NAMES, SH_ESSL_OUTPUT)
-    {}
+    RegenerateStructNamesTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT)
+    {
+        ShCompileOptions defaultCompileOptions      = {};
+        defaultCompileOptions.regenerateStructNames = true;
+        setDefaultCompileOptions(defaultCompileOptions);
+    }
 };
 
 // Test that a struct defined in a function scope is renamed. The global struct that's used as a
diff --git a/src/tests/compiler_tests/RemoveUnreferencedVariables_test.cpp b/src/tests/compiler_tests/RemoveUnreferencedVariables_test.cpp
index d10797c..e62d35d 100644
--- a/src/tests/compiler_tests/RemoveUnreferencedVariables_test.cpp
+++ b/src/tests/compiler_tests/RemoveUnreferencedVariables_test.cpp
@@ -17,8 +17,7 @@
 class RemoveUnreferencedVariablesTest : public MatchOutputCodeTest
 {
   public:
-    RemoveUnreferencedVariablesTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_ESSL_OUTPUT)
-    {}
+    RemoveUnreferencedVariablesTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT) {}
 };
 
 // Test that a simple unreferenced declaration is pruned.
diff --git a/src/tests/compiler_tests/RewriteDoWhile_test.cpp b/src/tests/compiler_tests/RewriteDoWhile_test.cpp
index bae95b2..cfa05fd 100644
--- a/src/tests/compiler_tests/RewriteDoWhile_test.cpp
+++ b/src/tests/compiler_tests/RewriteDoWhile_test.cpp
@@ -25,7 +25,7 @@
 
     void SetUp() override
     {
-        mExtraCompileOptions |= SH_REWRITE_DO_WHILE_LOOPS;
+        mCompileOptions.rewriteDoWhileLoops = true;
         ShaderCompileTreeTest::SetUp();
     }
 };
diff --git a/src/tests/compiler_tests/ScalarizeVecAndMatConstructorArgs_test.cpp b/src/tests/compiler_tests/ScalarizeVecAndMatConstructorArgs_test.cpp
index 80b0582..70ce712 100644
--- a/src/tests/compiler_tests/ScalarizeVecAndMatConstructorArgs_test.cpp
+++ b/src/tests/compiler_tests/ScalarizeVecAndMatConstructorArgs_test.cpp
@@ -21,10 +21,12 @@
 {
   public:
     ScalarizeVecAndMatConstructorArgsTest()
-        : MatchOutputCodeTest(GL_FRAGMENT_SHADER,
-                              SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS,
-                              SH_ESSL_OUTPUT)
-    {}
+        : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT)
+    {
+        ShCompileOptions defaultCompileOptions                  = {};
+        defaultCompileOptions.scalarizeVecAndMatConstructorArgs = true;
+        setDefaultCompileOptions(defaultCompileOptions);
+    }
 };
 
 // Verifies scalarizing matrix inside a vector constructor.
diff --git a/src/tests/compiler_tests/ShCompile_test.cpp b/src/tests/compiler_tests/ShCompile_test.cpp
index bb39208..86ac99d 100644
--- a/src/tests/compiler_tests/ShCompile_test.cpp
+++ b/src/tests/compiler_tests/ShCompile_test.cpp
@@ -39,7 +39,11 @@
 
     void testCompile(const char **shaderStrings, int stringCount, bool expectation)
     {
-        ShCompileOptions options      = SH_OBJECT_CODE | SH_VARIABLES | SH_INIT_OUTPUT_VARIABLES;
+        ShCompileOptions options    = {};
+        options.objectCode          = true;
+        options.variables           = true;
+        options.initOutputVariables = true;
+
         bool success                  = sh::Compile(mCompiler, shaderStrings, stringCount, options);
         const std::string &compileLog = sh::GetInfoLog(mCompiler);
         EXPECT_EQ(expectation, success) << compileLog;
@@ -201,10 +205,13 @@
         {
             std::locale localizedLoc(locale);
 
+            ShCompileOptions compileOptions = {};
+            compileOptions.objectCode       = true;
+
             // std::locale::global() must be used instead of setlocale() to affect new streams'
             // default locale
             std::locale::global(std::locale::classic());
-            sh::Compile(mCompiler, parts, 1, SH_OBJECT_CODE);
+            sh::Compile(mCompiler, parts, 1, compileOptions);
             std::string referenceOut = sh::GetObjectCode(mCompiler);
             EXPECT_NE(referenceOut.find("1.9"), std::string::npos)
                 << "float formatted incorrectly with classic locale";
@@ -212,7 +219,7 @@
             sh::ClearResults(mCompiler);
 
             std::locale::global(localizedLoc);
-            sh::Compile(mCompiler, parts, 1, SH_OBJECT_CODE);
+            sh::Compile(mCompiler, parts, 1, compileOptions);
             std::string localizedOut = sh::GetObjectCode(mCompiler);
             EXPECT_NE(localizedOut.find("1.9"), std::string::npos)
                 << "float formatted incorrectly with locale (" << localizedLoc.name() << ") set";
diff --git a/src/tests/compiler_tests/ShaderImage_test.cpp b/src/tests/compiler_tests/ShaderImage_test.cpp
index 993b59e..6a19c86 100644
--- a/src/tests/compiler_tests/ShaderImage_test.cpp
+++ b/src/tests/compiler_tests/ShaderImage_test.cpp
@@ -124,7 +124,7 @@
     void SetUp() override
     {
         ShaderCompileTreeTest::SetUp();
-        mExtraCompileOptions |= SH_VARIABLES;
+        mCompileOptions.variables = true;
     }
 
     ::GLenum getShaderType() const override { return GL_COMPUTE_SHADER; }
diff --git a/src/tests/compiler_tests/ShaderValidation_test.cpp b/src/tests/compiler_tests/ShaderValidation_test.cpp
index a0afe0b..8b1ab75 100644
--- a/src/tests/compiler_tests/ShaderValidation_test.cpp
+++ b/src/tests/compiler_tests/ShaderValidation_test.cpp
@@ -86,7 +86,8 @@
 
     void SetUp() override
     {
-        mExtraCompileOptions |= (SH_VARIABLES | SH_ENFORCE_PACKING_RESTRICTIONS);
+        mCompileOptions.variables                  = true;
+        mCompileOptions.enforcePackingRestrictions = true;
         ShaderCompileTreeTest::SetUp();
     }
 
diff --git a/src/tests/compiler_tests/ShaderVariable_test.cpp b/src/tests/compiler_tests/ShaderVariable_test.cpp
index 2f354e2..a770e1c 100644
--- a/src/tests/compiler_tests/ShaderVariable_test.cpp
+++ b/src/tests/compiler_tests/ShaderVariable_test.cpp
@@ -15,7 +15,21 @@
 namespace sh
 {
 
-TEST(ShaderVariableTest, FindInfoByMappedName)
+class ShaderVariableTest : public testing::Test
+{
+  public:
+    ShaderVariableTest() : mVariablesCompileOptions{}, mObjectCodeCompileOptions{}
+    {
+        mVariablesCompileOptions.variables   = true;
+        mObjectCodeCompileOptions.objectCode = true;
+    }
+
+  protected:
+    ShCompileOptions mVariablesCompileOptions;
+    ShCompileOptions mObjectCodeCompileOptions;
+};
+
+TEST_F(ShaderVariableTest, FindInfoByMappedName)
 {
     // struct A {
     //   float x[2];
@@ -78,7 +92,7 @@
     EXPECT_STREQ("uni[0].a[1].y", originalFullName.c_str());
 }
 
-TEST(ShaderVariableTest, IsSameUniformWithDifferentFieldOrder)
+TEST_F(ShaderVariableTest, IsSameUniformWithDifferentFieldOrder)
 {
     // struct A {
     //   float x;
@@ -125,7 +139,7 @@
     EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
 }
 
-TEST(ShaderVariableTest, IsSameUniformWithDifferentStructNames)
+TEST_F(ShaderVariableTest, IsSameUniformWithDifferentStructNames)
 {
     // struct A {
     //   float x;
@@ -178,7 +192,7 @@
     EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
 }
 
-TEST(ShaderVariableTest, IsSameVaryingWithDifferentInvariance)
+TEST_F(ShaderVariableTest, IsSameVaryingWithDifferentInvariance)
 {
     // invariant varying float vary;
     ShaderVariable vx;
@@ -212,7 +226,7 @@
 }
 
 // Test that using invariant varyings doesn't trigger a double delete.
-TEST(ShaderVariableTest, InvariantDoubleDeleteBug)
+TEST_F(ShaderVariableTest, InvariantDoubleDeleteBug)
 {
     ShBuiltInResources resources;
     sh::InitBuiltInResources(&resources);
@@ -230,12 +244,12 @@
         "  gl_Position = position;\n"
         "}"};
 
-    EXPECT_TRUE(sh::Compile(compiler, program, 1, SH_OBJECT_CODE));
-    EXPECT_TRUE(sh::Compile(compiler, program, 1, SH_OBJECT_CODE));
+    EXPECT_TRUE(sh::Compile(compiler, program, 1, mObjectCodeCompileOptions));
+    EXPECT_TRUE(sh::Compile(compiler, program, 1, mObjectCodeCompileOptions));
     sh::Destruct(compiler);
 }
 
-TEST(ShaderVariableTest, IllegalInvariantVarying)
+TEST_F(ShaderVariableTest, IllegalInvariantVarying)
 {
     ShBuiltInResources resources;
     sh::InitBuiltInResources(&resources);
@@ -265,12 +279,12 @@
         "  gl_Position = v_varying;\n"
         "}"};
 
-    EXPECT_TRUE(sh::Compile(compiler, program1, 1, SH_VARIABLES));
-    EXPECT_FALSE(sh::Compile(compiler, program2, 1, SH_VARIABLES));
+    EXPECT_TRUE(sh::Compile(compiler, program1, 1, mVariablesCompileOptions));
+    EXPECT_FALSE(sh::Compile(compiler, program2, 1, mVariablesCompileOptions));
     sh::Destruct(compiler);
 }
 
-TEST(ShaderVariableTest, InvariantLeakAcrossShaders)
+TEST_F(ShaderVariableTest, InvariantLeakAcrossShaders)
 {
     ShBuiltInResources resources;
     sh::InitBuiltInResources(&resources);
@@ -291,7 +305,7 @@
         "  gl_Position = v_varying;\n"
         "}"};
 
-    EXPECT_TRUE(sh::Compile(compiler, program1, 1, SH_VARIABLES));
+    EXPECT_TRUE(sh::Compile(compiler, program1, 1, mVariablesCompileOptions));
     const std::vector<sh::ShaderVariable> *varyings = sh::GetOutputVaryings(compiler);
     for (const sh::ShaderVariable &varying : *varyings)
     {
@@ -300,7 +314,7 @@
             EXPECT_TRUE(varying.isInvariant);
         }
     }
-    EXPECT_TRUE(sh::Compile(compiler, program2, 1, SH_VARIABLES));
+    EXPECT_TRUE(sh::Compile(compiler, program2, 1, mVariablesCompileOptions));
     varyings = sh::GetOutputVaryings(compiler);
     for (const sh::ShaderVariable &varying : *varyings)
     {
@@ -312,7 +326,7 @@
     sh::Destruct(compiler);
 }
 
-TEST(ShaderVariableTest, GlobalInvariantLeakAcrossShaders)
+TEST_F(ShaderVariableTest, GlobalInvariantLeakAcrossShaders)
 {
     ShBuiltInResources resources;
     sh::InitBuiltInResources(&resources);
@@ -333,7 +347,7 @@
         "  gl_Position = v_varying;\n"
         "}"};
 
-    EXPECT_TRUE(sh::Compile(compiler, program1, 1, SH_VARIABLES));
+    EXPECT_TRUE(sh::Compile(compiler, program1, 1, mVariablesCompileOptions));
     const std::vector<sh::ShaderVariable> *varyings = sh::GetOutputVaryings(compiler);
     for (const sh::ShaderVariable &varying : *varyings)
     {
@@ -342,7 +356,7 @@
             EXPECT_TRUE(varying.isInvariant);
         }
     }
-    EXPECT_TRUE(sh::Compile(compiler, program2, 1, SH_VARIABLES));
+    EXPECT_TRUE(sh::Compile(compiler, program2, 1, mVariablesCompileOptions));
     varyings = sh::GetOutputVaryings(compiler);
     for (const sh::ShaderVariable &varying : *varyings)
     {
@@ -354,9 +368,8 @@
     sh::Destruct(compiler);
 }
 
-TEST(ShaderVariableTest, BuiltinInvariantVarying)
+TEST_F(ShaderVariableTest, BuiltinInvariantVarying)
 {
-
     ShBuiltInResources resources;
     sh::InitBuiltInResources(&resources);
 
@@ -379,7 +392,7 @@
         "  gl_Position = vec4(0, 0, 0, 0);\n"
         "}"};
 
-    EXPECT_TRUE(sh::Compile(compiler, program1, 1, SH_VARIABLES));
+    EXPECT_TRUE(sh::Compile(compiler, program1, 1, mVariablesCompileOptions));
     const std::vector<sh::ShaderVariable> *varyings = sh::GetOutputVaryings(compiler);
     for (const sh::ShaderVariable &varying : *varyings)
     {
@@ -388,7 +401,7 @@
             EXPECT_TRUE(varying.isInvariant);
         }
     }
-    EXPECT_TRUE(sh::Compile(compiler, program2, 1, SH_VARIABLES));
+    EXPECT_TRUE(sh::Compile(compiler, program2, 1, mVariablesCompileOptions));
     varyings = sh::GetOutputVaryings(compiler);
     for (const sh::ShaderVariable &varying : *varyings)
     {
@@ -397,12 +410,12 @@
             EXPECT_FALSE(varying.isInvariant);
         }
     }
-    EXPECT_FALSE(sh::Compile(compiler, program3, 1, SH_VARIABLES));
+    EXPECT_FALSE(sh::Compile(compiler, program3, 1, mVariablesCompileOptions));
     sh::Destruct(compiler);
 }
 
 // Verify in ES3.1 two varyings with either same name or same declared location can match.
-TEST(ShaderVariableTest, IsSameVaryingWithDifferentName)
+TEST_F(ShaderVariableTest, IsSameVaryingWithDifferentName)
 {
     // Varying float vary1;
     ShaderVariable vx;
@@ -449,7 +462,7 @@
 }
 
 // Test that using two consecutive underscores (__) can be used for declaring an identifier
-TEST(ShaderVariableTest, DoubleUnderscoresForIdentifier)
+TEST_F(ShaderVariableTest, DoubleUnderscoresForIdentifier)
 {
     ShBuiltInResources resources;
     sh::InitBuiltInResources(&resources);
@@ -466,7 +479,7 @@
         "  v = 1.0;\n"
         "  gl_Position = __position;\n"
         "}"};
-    EXPECT_TRUE(sh::Compile(compiler, front_underscores, 1, SH_OBJECT_CODE));
+    EXPECT_TRUE(sh::Compile(compiler, front_underscores, 1, mObjectCodeCompileOptions));
 
     const char *middle_underscores[] = {
         "#version 300 es\n"
@@ -476,7 +489,7 @@
         "  v = 1.0;\n"
         "  gl_Position = position__in;\n"
         "}"};
-    EXPECT_TRUE(sh::Compile(compiler, middle_underscores, 1, SH_OBJECT_CODE));
+    EXPECT_TRUE(sh::Compile(compiler, middle_underscores, 1, mObjectCodeCompileOptions));
 
     const char *end_underscores[] = {
         "#version 300 es\n"
@@ -486,7 +499,7 @@
         "  v = 1.0;\n"
         "  gl_Position = position__;\n"
         "}"};
-    EXPECT_TRUE(sh::Compile(compiler, end_underscores, 1, SH_OBJECT_CODE));
+    EXPECT_TRUE(sh::Compile(compiler, end_underscores, 1, mObjectCodeCompileOptions));
 
     sh::Destruct(compiler);
 }
diff --git a/src/tests/compiler_tests/TypeTracking_test.cpp b/src/tests/compiler_tests/TypeTracking_test.cpp
index 155d9b3..b631b08 100644
--- a/src/tests/compiler_tests/TypeTracking_test.cpp
+++ b/src/tests/compiler_tests/TypeTracking_test.cpp
@@ -35,8 +35,11 @@
 
     void compile(const std::string &shaderString)
     {
+        ShCompileOptions compileOptions = {};
+        compileOptions.intermediateTree = true;
+
         const char *shaderStrings[] = {shaderString.c_str()};
-        bool compilationSuccess     = mTranslator->compile(shaderStrings, 1, SH_INTERMEDIATE_TREE);
+        bool compilationSuccess     = mTranslator->compile(shaderStrings, 1, compileOptions);
         TInfoSink &infoSink         = mTranslator->getInfoSink();
         mInfoLog                    = RemoveSymbolIdsFromInfoLog(infoSink.info.c_str());
         if (!compilationSuccess)
diff --git a/src/tests/compiler_tests/UnfoldShortCircuitAST_test.cpp b/src/tests/compiler_tests/UnfoldShortCircuitAST_test.cpp
index 1463fe1..ddaf564 100644
--- a/src/tests/compiler_tests/UnfoldShortCircuitAST_test.cpp
+++ b/src/tests/compiler_tests/UnfoldShortCircuitAST_test.cpp
@@ -4,7 +4,7 @@
 // found in the LICENSE file.
 //
 // UnfoldShortCircuitAST_test.cpp:
-//  Tests shader compilation with SH_UNFOLD_SHORT_CIRCUIT workaround on.
+//  Tests shader compilation with unfoldShortCircuit workaround on.
 
 #include "GLSLANG/ShaderLang.h"
 #include "angle_gl.h"
@@ -16,9 +16,12 @@
 class UnfoldShortCircuitASTTest : public MatchOutputCodeTest
 {
   public:
-    UnfoldShortCircuitASTTest()
-        : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_UNFOLD_SHORT_CIRCUIT, SH_GLSL_330_CORE_OUTPUT)
-    {}
+    UnfoldShortCircuitASTTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_GLSL_330_CORE_OUTPUT)
+    {
+        ShCompileOptions defaultCompileOptions   = {};
+        defaultCompileOptions.unfoldShortCircuit = true;
+        setDefaultCompileOptions(defaultCompileOptions);
+    }
 };
 
 // Test unfolding the && operator.
diff --git a/src/tests/compiler_tests/UnrollFlatten_test.cpp b/src/tests/compiler_tests/UnrollFlatten_test.cpp
index 47ba192..badf7de 100644
--- a/src/tests/compiler_tests/UnrollFlatten_test.cpp
+++ b/src/tests/compiler_tests/UnrollFlatten_test.cpp
@@ -28,10 +28,13 @@
   protected:
     void compile(const std::string &shaderString)
     {
+        ShCompileOptions compileOptions = {};
+        compileOptions.variables        = true;
+
         std::string infoLog;
         bool compilationSuccess =
             compileTestShader(GL_FRAGMENT_SHADER, mInputSpec, SH_HLSL_4_1_OUTPUT, shaderString,
-                              SH_VARIABLES, &mTranslatedSource, &infoLog);
+                              compileOptions, &mTranslatedSource, &infoLog);
         if (!compilationSuccess)
         {
             FAIL() << "Shader compilation failed " << infoLog;
diff --git a/src/tests/compiler_tests/WorkGroupSize_test.cpp b/src/tests/compiler_tests/WorkGroupSize_test.cpp
index 90e033a..02b0ecf 100644
--- a/src/tests/compiler_tests/WorkGroupSize_test.cpp
+++ b/src/tests/compiler_tests/WorkGroupSize_test.cpp
@@ -35,10 +35,14 @@
     // Return true when compilation succeeds
     bool compile(const std::string &shaderString)
     {
+        ShCompileOptions compileOptions = {};
+        compileOptions.variables        = true;
+        compileOptions.intermediateTree = true;
+
         const char *shaderStrings[] = {shaderString.c_str()};
-        bool status = mTranslator->compile(shaderStrings, 1, SH_INTERMEDIATE_TREE | SH_VARIABLES);
-        TInfoSink &infoSink = mTranslator->getInfoSink();
-        mInfoLog            = infoSink.info.c_str();
+        bool status                 = mTranslator->compile(shaderStrings, 1, compileOptions);
+        TInfoSink &infoSink         = mTranslator->getInfoSink();
+        mInfoLog                    = infoSink.info.c_str();
         return status;
     }
 
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
index 28a09f7..0f9714e 100644
--- a/src/tests/gl_tests/GLSLTest.cpp
+++ b/src/tests/gl_tests/GLSLTest.cpp
@@ -9785,7 +9785,7 @@
     ANGLE_GL_PROGRAM(program, kVS, kFS);
 }
 
-// Test that inactive output variables compile ok in combination with SH_INIT_OUTPUT_VARIABLES
+// Test that inactive output variables compile ok in combination with initOutputVariables
 // (which is enabled on WebGL).
 TEST_P(WebGL2GLSLTest, InactiveOutput)
 {
diff --git a/src/tests/perf_tests/CompilerPerf.cpp b/src/tests/perf_tests/CompilerPerf.cpp
index 4568bb7..e0479eb 100644
--- a/src/tests/perf_tests/CompilerPerf.cpp
+++ b/src/tests/perf_tests/CompilerPerf.cpp
@@ -305,8 +305,11 @@
 {
     const char *shaderStrings[] = {mTestShader};
 
-    ShCompileOptions compileOptions = SH_OBJECT_CODE | SH_VARIABLES |
-                                      SH_INITIALIZE_UNINITIALIZED_LOCALS | SH_INIT_OUTPUT_VARIABLES;
+    ShCompileOptions compileOptions              = {};
+    compileOptions.objectCode                    = true;
+    compileOptions.variables                     = true;
+    compileOptions.initializeUninitializedLocals = true;
+    compileOptions.initOutputVariables           = true;
 
 #if !defined(NDEBUG)
     // Make sure that compilation succeeds and print the info log if it doesn't in debug mode.
diff --git a/src/tests/test_utils/ShaderCompileTreeTest.cpp b/src/tests/test_utils/ShaderCompileTreeTest.cpp
index 2aa4ab9..4f716cd 100644
--- a/src/tests/test_utils/ShaderCompileTreeTest.cpp
+++ b/src/tests/test_utils/ShaderCompileTreeTest.cpp
@@ -134,7 +134,7 @@
 bool ShaderCompileTreeTest::compile(const std::string &shaderString)
 {
     const char *shaderStrings[] = {shaderString.c_str()};
-    mASTRoot = mTranslator->compileTreeForTesting(shaderStrings, 1, mExtraCompileOptions);
+    mASTRoot            = mTranslator->compileTreeForTesting(shaderStrings, 1, mCompileOptions);
     TInfoSink &infoSink = mTranslator->getInfoSink();
     mInfoLog            = infoSink.info.c_str();
     return mASTRoot != nullptr;
@@ -155,13 +155,13 @@
 
 const std::vector<sh::ShaderVariable> &ShaderCompileTreeTest::getUniforms() const
 {
-    ASSERT(mExtraCompileOptions & SH_VARIABLES);
+    ASSERT(mCompileOptions.variables);
     return mTranslator->getUniforms();
 }
 
 const std::vector<sh::ShaderVariable> &ShaderCompileTreeTest::getAttributes() const
 {
-    ASSERT(mExtraCompileOptions & SH_VARIABLES);
+    ASSERT(mCompileOptions.variables);
     return mTranslator->getAttributes();
 }
 
diff --git a/src/tests/test_utils/ShaderCompileTreeTest.h b/src/tests/test_utils/ShaderCompileTreeTest.h
index ac01a32..2be289d 100644
--- a/src/tests/test_utils/ShaderCompileTreeTest.h
+++ b/src/tests/test_utils/ShaderCompileTreeTest.h
@@ -25,7 +25,7 @@
 class ShaderCompileTreeTest : public testing::Test
 {
   public:
-    ShaderCompileTreeTest() : mExtraCompileOptions(0) {}
+    ShaderCompileTreeTest() : mCompileOptions{} {}
 
   protected:
     void SetUp() override;
@@ -46,7 +46,7 @@
     virtual ShShaderSpec getShaderSpec() const = 0;
 
     std::string mInfoLog;
-    ShCompileOptions mExtraCompileOptions;
+    ShCompileOptions mCompileOptions;
 
     TIntermBlock *mASTRoot;
 
diff --git a/src/tests/test_utils/ShaderExtensionTest.h b/src/tests/test_utils/ShaderExtensionTest.h
index b1c2b16..b6bc843 100644
--- a/src/tests/test_utils/ShaderExtensionTest.h
+++ b/src/tests/test_utils/ShaderExtensionTest.h
@@ -63,8 +63,11 @@
                                                const char *pragma,
                                                const char *shader)
     {
-        const char *shaderStrings[] = {version, pragma, shader};
-        bool success = sh::Compile(mCompiler, shaderStrings, 3, SH_OBJECT_CODE | SH_VARIABLES);
+        const char *shaderStrings[]     = {version, pragma, shader};
+        ShCompileOptions compileOptions = {};
+        compileOptions.objectCode       = true;
+        compileOptions.variables        = true;
+        bool success                    = sh::Compile(mCompiler, shaderStrings, 3, compileOptions);
         if (success)
         {
             return ::testing::AssertionSuccess() << "Compilation success";
diff --git a/src/tests/test_utils/compiler_test.cpp b/src/tests/test_utils/compiler_test.cpp
index 28f2705..d5e6493 100644
--- a/src/tests/test_utils/compiler_test.cpp
+++ b/src/tests/test_utils/compiler_test.cpp
@@ -65,7 +65,7 @@
                        ShShaderOutput output,
                        const std::string &shaderString,
                        ShBuiltInResources *resources,
-                       ShCompileOptions compileOptions,
+                       const ShCompileOptions &compileOptions,
                        std::string *translatedCode,
                        std::string *infoLog)
 {
@@ -78,9 +78,10 @@
 
     const char *shaderStrings[] = {shaderString.c_str()};
 
-    bool compilationSuccess =
-        translator->compile(shaderStrings, 1, SH_OBJECT_CODE | compileOptions);
-    TInfoSink &infoSink = translator->getInfoSink();
+    ShCompileOptions options = compileOptions;
+    options.objectCode       = true;
+    bool compilationSuccess  = translator->compile(shaderStrings, 1, options);
+    TInfoSink &infoSink      = translator->getInfoSink();
     if (translatedCode)
     {
         *translatedCode = infoSink.obj.isBinary() ? kBinaryBlob : infoSink.obj.c_str();
@@ -97,7 +98,7 @@
                        ShShaderSpec spec,
                        ShShaderOutput output,
                        const std::string &shaderString,
-                       ShCompileOptions compileOptions,
+                       const ShCompileOptions &compileOptions,
                        std::string *translatedCode,
                        std::string *infoLog)
 {
@@ -108,16 +109,19 @@
                              translatedCode, infoLog);
 }
 
-MatchOutputCodeTest::MatchOutputCodeTest(GLenum shaderType,
-                                         ShCompileOptions defaultCompileOptions,
-                                         ShShaderOutput outputType)
-    : mShaderType(shaderType), mDefaultCompileOptions(defaultCompileOptions)
+MatchOutputCodeTest::MatchOutputCodeTest(GLenum shaderType, ShShaderOutput outputType)
+    : mShaderType(shaderType), mDefaultCompileOptions{}
 {
     sh::InitBuiltInResources(&mResources);
     mResources.FragmentPrecisionHigh = 1;
     mOutputCode[outputType]          = std::string();
 }
 
+void MatchOutputCodeTest::setDefaultCompileOptions(const ShCompileOptions &defaultCompileOptions)
+{
+    mDefaultCompileOptions = defaultCompileOptions;
+}
+
 void MatchOutputCodeTest::addOutputType(const ShShaderOutput outputType)
 {
     mOutputCode[outputType] = std::string();
@@ -134,7 +138,7 @@
 }
 
 void MatchOutputCodeTest::compile(const std::string &shaderString,
-                                  const ShCompileOptions compileOptions)
+                                  const ShCompileOptions &compileOptions)
 {
     std::string infoLog;
     for (auto &code : mOutputCode)
@@ -150,7 +154,7 @@
 
 bool MatchOutputCodeTest::compileWithSettings(ShShaderOutput output,
                                               const std::string &shaderString,
-                                              const ShCompileOptions compileOptions,
+                                              const ShCompileOptions &compileOptions,
                                               std::string *translatedCode,
                                               std::string *infoLog)
 {
diff --git a/src/tests/test_utils/compiler_test.h b/src/tests/test_utils/compiler_test.h
index fd2dfc1..7311885 100644
--- a/src/tests/test_utils/compiler_test.h
+++ b/src/tests/test_utils/compiler_test.h
@@ -28,7 +28,7 @@
                        ShShaderOutput output,
                        const std::string &shaderString,
                        ShBuiltInResources *resources,
-                       ShCompileOptions compileOptions,
+                       const ShCompileOptions &compileOptions,
                        std::string *translatedCode,
                        std::string *infoLog);
 
@@ -36,24 +36,23 @@
                        ShShaderSpec spec,
                        ShShaderOutput output,
                        const std::string &shaderString,
-                       ShCompileOptions compileOptions,
+                       const ShCompileOptions &compileOptions,
                        std::string *translatedCode,
                        std::string *infoLog);
 
 class MatchOutputCodeTest : public testing::Test
 {
   protected:
-    MatchOutputCodeTest(GLenum shaderType,
-                        ShCompileOptions defaultCompileOptions,
-                        ShShaderOutput outputType);
+    MatchOutputCodeTest(GLenum shaderType, ShShaderOutput outputType);
 
+    void setDefaultCompileOptions(const ShCompileOptions &defaultCompileOptions);
     void addOutputType(const ShShaderOutput outputType);
 
     ShBuiltInResources *getResources();
 
     // Compile functions clear any results from earlier calls to them.
     void compile(const std::string &shaderString);
-    void compile(const std::string &shaderString, const ShCompileOptions compileOptions);
+    void compile(const std::string &shaderString, const ShCompileOptions &compileOptions);
 
     bool foundInESSLCode(const char *stringToFind) const
     {
@@ -94,7 +93,7 @@
   private:
     bool compileWithSettings(ShShaderOutput output,
                              const std::string &shaderString,
-                             ShCompileOptions compileOptions,
+                             const ShCompileOptions &compileOptions,
                              std::string *translatedCode,
                              std::string *infoLog);